Шаблоны проектирования JavaScript, часть 1: фабричный шаблон
В последнее время, когда проекты, над которыми у меня была возможность работать, выросли в масштабе, я нашел время, чтобы глубже погрузиться в шаблоны проектирования для написания более удобного в сопровождении и масштабируемого кода Javascript. Шаблоны проектирования — это отличный способ применить проверенные временем и боем решения для общих проблем, чтобы мы могли быстрее и эффективнее их решать.
Большинство шаблонов проектирования, которые мы рассмотрим, основаны на объектно-ориентированном программировании, и поэтому имеет смысл только начать с рассмотрения шаблона создания, названного так потому, что шаблон предоставляет нам четкий интерфейс для создания объектов при абстрагировании. разнообразная сложность или логика, связанные с их созданием. Этот шаблон называется Factory Pattern и позволяет нам легко создавать объекты в JavaScript.
Исходя из других языков ООП, основанных на классах, может возникнуть соблазн подумать, что в приведенных ниже строках кода мы будем создавать классы и экземпляры, но на самом деле это просто синтаксический сахар, сделанный для того, чтобы выглядеть как синтаксис из класса. основанный язык.
На самом деле мы используем прототипное наследование JavaScript и OLOO — Objects Linking to Other Objects для создания объектов с общим прототипом. Сам прототип — это просто объект JavaScript, а не класс в прямом смысле этого слова. Отличное объяснение наследования в Javascript и его отличий от классического наследования можно найти в статье Эрика Эллиота здесь.
Давайте углубимся в код.
Все примеры из этой серии будут доступны на Github здесь и включают инструкции по запуску кода.
Чтобы запустить код из этой статьи, на вашем компьютере должен быть установлен Node. Следуйте этим инструкциям, если у вас его еще нет. Если вы следуете репозиторию, вы найдете инструкции по запуску кода в файле readme.
Перво-наперво, давайте создадим папку. Мы можем назвать это javascript-design-patterns в этой папке, мы создадим фабричную папку.
Фабричный паттерн в действии
Фабричный шаблон оборачивает конструктор для различных типов объектов и возвращает экземпляры объектов через простой API. Это упрощает создание различных объектов, предоставляя простой API, возвращающий указанный тип объекта.
Начнем с создания наших конструкторов. Эти функции будут отвечать за возврат новых объектов определенного типа при вызове.
В папке factory создадим файл Laptop.js.
const Laptop = function({ ram, hdd, name }) {
this.ram = ram || 0;
this.hdd = hdd || 0;
this.name = name || "";
};
module.exports = Laptop;
В этом файле мы создаем функцию конструктора ноутбука. Он принимает объект в качестве параметра с атрибутами для создания экземпляра объекта с различными характеристиками, которые мы хотим захватить — в данном случае это размер ОЗУ, размер жесткого диска и имя устройства.
После этого мы экспортируем из модуля функцию-конструктор Laptop.
Давайте создадим еще один файл с именем table.js.
Мы сделаем то же самое, но со спецификациями, более подходящими для планшета.
const Tablet = function({ ram, hdd, name, network }) {
this.ram = ram || 0;
this.hdd = hdd || 0;
this.network = network || 0;
this.name = name || "";
};
module.exports = Tablet;
Теперь, когда у нас есть наши конструкторы, давайте создадим фабричную функцию, которая будет предоставлять API для создания новых экземпляров этих элементов. Добавьте новый файл с именем gadgetFactory.js.
const Laptop = require("./laptop");
const Tablet = require("./tablet");
const gadget = { Laptop, Tablet };
module.exports = {
createGadget(type, attributes) {
const GadgetType = gadget[type];
return new GadgetType(attributes);
}
};
Здесь мы начинаем с импорта конструкторов для создания объектов Laptop и Tablet. Затем мы создаем объект гаджета, используя имена конструкторов в качестве ключей. Это позволяет нам получить доступ к типу конструктора, который мы хотим, с помощью гаджета.[type]- где в этом примере тип будет «Ноутбук» или «Планшет».
Наконец, мы экспортируем объект из этого модуля с помощью метода createGadget. Этот метод принимает тип гаджета в качестве первого параметра и вызывает указанный тип конструктора, передавая ему атрибуты.
Следует отметить, что когда мы вызываем функцию с ключевым словом new в Javascript, мы получаем взамен пустой объект с привязкой this, установленной на ту, что находится в выполняемой функции. Этот уникальный вызов также создаст прототипную связь между функцией-конструктором и любыми новыми объектами, которые мы создаем таким образом. Мы увидим это подробно в других шаблонах проектирования, которые мы рассмотрим.
Также стоит отметить, что первая заглавная буква — это просто соглашение, а не требование. Он не делает ничего особенного, и мы могли бы с таким же успехом называть функции с помощью camelCase, как мы обычно делаем с другими именами переменных и функций в JavaScript.
Теперь мы можем создать файл, который будет использовать (или потреблять) API нашего заводского шаблона.
Создайте файл index.js и добавьте следующий код.
const gadgetFactory = require("./gadgetFactory");
const myLaptop = gadgetFactory.createGadget("Laptop", {
ram: 8,
ssd: 256,
name: "Bab's MacBook Pro"
});
const myTablet = gadgetFactory.createGadget("Tablet", {
ram: 4,
hdd: 128,
name: "Bab's iPad",
network: '4G'
});
console.log(myLaptop);
console.log(myTablet);
Первое, что вы можете заметить, это то, что в этом файле нам не нужны конструкторы для ноутбуков и планшетов напрямую. Все, что нам нужно, это модуль gadgetFactory (с его методом createGadget). Используя этот метод, мы затем создаем два экземпляра ноутбука и планшета соответственно и выводим их на консоль.
Теперь в вашем терминале перейдите в папку javascript-design-patterns и введите:
$ node ./factory/index.js
Вы должны увидеть следующее, зарегистрированное в консоли:
Laptop { ram: 8, ssd: 256, name: 'Bab\'s MacBook Pro' }
Tablet { ram: 4, hdd: 128, network: '4G', name: 'Bab\'s iPad' }
Как видите, мы создали один тип объекта «Ноутбук» и один тип «Планшет», каждый со своими спецификациями. Используя этот шаблон, вы можете создать столько объектов гаджетов, сколько вам нужно, каждый со своими характеристиками.
И это все для заводского шаблона. Конечно, это довольно упрощенная реализация, и во что-либо, кроме тривиального приложения, вы определенно захотите включить более строгую логику — например, вокруг своих конструкторов.
В этом примере мы использовали функции-конструкторы Javascript, но этот шаблон также можно реализовать с помощью прототипов.