В этой статье мы расскажем об общих шаблонах проектирования в JS. Эти шаблоны предлагают разработчикам способы решения технических проблем многоразовыми и элегантными способами. Хотите улучшить ваши JavaScript навыки? Тогда читайте дальше.
Что такое шаблон проектрирования или паттерн?
Проще говоря, дизайн паттерн представляет собой многократно используемое программное решение для определенного типа проблемы, которая часто возникает при разработке программного обеспечения. За много лет практики разработки ПО, эксперты выяснили пути решения подобных проблем. Эти решения были инкапсулированы в шаблоны дизайна.
Итак:
- шаблоны – это проверенное решения проблем разработки программного обеспечения
- шаблоны являются масштабируемыми, поскольку они обычно структурированы и имеют правила, которых вы должны придерживаться
- шаблоны могут быть повторно использованы для аналогичных задач
Разновидности шаблонов проектирования
При разработке программного обеспечения, дизайн паттерны, как правило, сгруппированы в несколько категорий. В этой статье, мы рассмотрим три наиболее важных.
Вкратце о них:
- Порождающие шаблоны (creational patterns) сосредоточены на способах создания объектов или классов. Это может показаться простым (и это в некоторых случаях оно так и есть), но большие приложения должны контролировать процесс создания объекта.
- Структурные шаблоны (structural design patterns) сосредоточены на том, чтобы управлять отношениями между объектами так, чтобы ваше приложение было построено масштабируемым способом. Ключевым аспектом структурной модели является обеспечение того, что изменение в одной части приложения не влияет на все другие части.
- Поведенческие шаблоны (behavioral patterns) сосредоточены на связи между объектами
Примечание о классах в JavaScript
Читая о дизайн шаблонах, вы часто будете видеть ссылки на классы и объекты. Это может привести к путанице, поскольку JavaScript на самом деле не использует “class” (класс), более правильным является термин “data type” (тип данных).
Типы данных в JavaScript
JavaScript является объектно-ориентированным языком, где объекты наследуют от других объектов, в концепции известной как прототипное наследство. Типы данных (data types) могут быть созданы путем определения того, что называется “функцией конструктора”, например:
function Person(config) { this.name = config.name; this.age = config.age; } Person.prototype.getAge = function() { return this.age; }; var tilo = new Person({name:"Tilo", age:23 }); console.log(tilo.getAge());
Обратите внимание на использование prototype при определении методов на Person типе данных. Так как несколько Person объектов будут ссылаться на тот же прототип, это позволит getAge() методу быть разделенным всеми экземплярами Person типа данных, нежели его переопределения для каждого экземпляра. Кроме того, любой тип данных, который наследует от Person, будет иметь доступ к методу getAge().
Работа с конфиденциальностью
Другой распространенной проблемой в JavaScript является то, что у него нет никаких приватных переменных. Но, мы можем использовать несколько замыканий для имитации конфиденциальности. Рассмотрим следующий сниппет:
var retinaMacbook = (function() { //Приватные переменные var RAM, addRAM; RAM = 4; //Приватные методы addRAM = function (additionalRAM) { RAM += additionalRAM; }; return { //Публичные переменные и методы USB: undefined, insertUSB: function (device) { this.USB = device; }, removeUSB: function () { var device = this.USB; this.USB = undefined; return device; } }; })();
В приведенном выше примере, мы создали retinaMacbook объект, с публичными и приватными переменными и методами. Вот, как мы будем это использовать:
retinaMacbook.insertUSB("myUSB"); console.log(retinaMacbook.USB); //logs out "myUSB" console.log(retinaMacbook.RAM) //logs out undefined
Порождающие шаблоны
Существует много различных видов порождающих шаблонов (абстрактная фабрика, строитель, фабричный метод, отложенная инициализация, пул одиночек, объектный пул, прототип, получение ресурса есть инициализация, одиночка), но в этом уроке, мы только рассмотрим два из них: Строитель (Builder) и Прототип(Prototype). Они используются достаточно часто, чтобы заработать к себе внимание.
Строительный шаблон (Builder pattern)
Строительный шаблон часто используется в веб-разработке, и вы, наверняка, использовали его раньше не осознавая этого. Проще говоря, этот шаблон может быть определен следующим образом:
Применение строительного шаблона позволяет строить объекты, указав только тип и содержание объекта. Нет необходимости создания объекта.
Например, вы, наверняка, делали это бесчисленное количество раз в jQuery:
var myDiv = $('<div id="myDiv">This is a div.</div>'); //myDiv теперь представляет jQuery объект ссылающейся на DOM узел. var someText = $('<p/>'); //someText это jQuery объект ссылающейся на HTMLParagraphElement var input = $('<input />');
Взгляните на три примера выше. В первом из них, мы прошли в <div/> элемент с некоторым контентом. Во втором, мы прошли в пустой тег <p>. В последнем, мы прошли в <input /> элемент. Результат всех трех, был одинаковым, – нам был возвращен jQuery объект, ссылающийся на узел DOM.
Переменная $ адаптирует строительный шаблон в jQuery. В каждом примере, нам был возвращен jQuery DOM объекти и имелся доступ ко всем методам, предоставляемых библиотекой jQuery, и не в одном из моментов, мы не вызывали document.createElement. JS library обработала все это за закрытыми дверями.
Представьте себе, как много было бы работы, если нам пришлось бы создавать элемент DOM и вставлять содержимое в него! Используя строительный шаблон, мы можем сосредоточить свое внимание на типе и содержание объекта, а не на его явном создании.
Прототипный шаблон (Prototype pattern)
Ранее мы рассмотрели процесс по определению типов данных в JavaScript через функции и добавления методов в прототип объекта. Прототипные шаблоны (схемы), позволяют объектам наследовать от других объектов, через их прототипы.
Прототипный шаблон – это шаблон, в котором объекты создаются на основе шаблона существующего объекта путем клонирования.
Это простой и естественный способ реализации наследования в JavaScript. Например:
var Person = { numFeet: 2, numHeads: 1, numHands:2 }; //Object.create берет свой первый аргумент и применяет его к прототипу нового объекта. var tilo = Object.create(Person); console.log(tilo.numHeads); //результат 1 tilo.numHeads = 2; console.log(tilo.numHeads) //результат 2
Свойства (и методы) в Person объекте, применяются к прототипу объекта tilo. Мы можем переопределить свойства объекта tilo, если хотим, чтобы они были разными.
В приведенном выше примере, мы использовали Object.create (). Однако, Internet Explorer 8 не поддерживает новый метод. В этих случаях мы можем имитировать его поведение:
var vehiclePrototype = { init: function (carModel) { this.model = carModel; }, getModel: function () { console.log( "The model of this vehicle is " + this.model); } }; function vehicle (model) { function F() {}; F.prototype = vehiclePrototype; var f = new F(); f.init(model); return f; } var car = vehicle("Ford Escort"); car.getModel();
Структурные шаблоны
Структурные шаблоны действительно полезны, особенно при выяснении, как система должна работать. Они позволяют приложениям легко масштабироваться и оставаться управляемыми. Из этой немалой группы (адаптер, мост, компоновщик, декоратор, фасад, единая точка входа, приспособленец, заместитель), мы рассмотрим следующие модели: Компоновщик и Фасад.
Компоновщик (шаблон проектирования)
- Шаблон Компоновщик, – это ещё один вид шаблона, который вы, вероятно, использовали без осознания того.
Итак, что же это значит? Давайте, рассмотрим следующий пример в jQuery (у большинства JS библиотек будут эквиваленты этому):
$('.myList').addClass('selected'); $('#myItem').addClass('selected'); //Не делайте этого на больших таблицах, это всего лишь пример. $("#dataTable tbody tr").on("click", function(event){ alert($(this).text()); }); $('#myButton').on("click", function(event) { alert("Clicked."); });
Большинство библиотек JavaScript обеспечивают последовательное API, независимо от того, имеем мы дело с одним элементом DOM или массивом DOM элементов. В первом примере, мы можем добавить selected класс ко все элементам подобраных селектором .myList, но мы также можем использовать этот же метод, когда речь идет об еденичном DOM элементе, #myItem. Точно так же можно приложить обработчик событий с помощью on() метода на нескольких узлах, или на одном узле через тот же API.
Благодаря использованию композитных макетов, jQuery (и многие другие библиотеки) предоставляют нам упрощенный API.
Composite шаблон иногда может вызывать проблемы. В слабо расписанном языке, таком как JavaScript, полезным будет знать, имеем ли мы дело с одним элементом или несколькими элементами. Так как компоновщик шаблон использует одинаковый API для обоих, мы зачастую можем принять одно за другое и в конечном итоге столкнуться с неожиданной ошибкой. Некоторым библиотекам, таким как YUI3, предлагают два отдельных метода получения элементов ( Y.one() vs Y.all()).
Фасад (шаблон проектирования)
Вот еще один паттерн, который мы воспринимаем как должное. Объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое.
Фасад паттерн предоставляет пользователю простой интерфейс, скрыв его основные сложности.
Фасад шаблон, почти всегда улучшает удобство использования части программного обеспечения. Использование jQuery в качестве примера, одним из наиболее распространенных методов библиотеки, является ready() метод:
$(document).ready(function() { //весь ваш код идет сюда... });
Метод ready() фактически реализует фасад. Если взглянуть на источник, вот что вы найдете:
ready: (function() { ... //Mozilla, Opera, и Webkit if (document.addEventListener) { document.addEventListener("DOMContentLoaded", idempotent_fn, false); ... } //IE модель событий else if (document.attachEvent) { // обеспечьте firing до onload; может быть поздно, но безопасно для iframes document.attachEvent("onreadystatechange", idempotent_fn); // Резерв для window.onload, который всегда работает window.attachEvent("onload", idempotent_fn); ... } })
Метод ready() не такой уж простой. jQuery нормализует непостоянство браузера, чтобы ready() сработал в нужный момент. Однако, как разработчик, вы будете представлены с простым интерфейсом.
Большинство примеров фасад шаблонов, следует этому принципу. Для его реализации, мы обычно полагаемся на условные операторы, но представляем его в виде простого интерфейса для пользователя. Другие методы реализации этого паттерна включают в себя animate() и css().
Поведенческие шаблоны
Любая объектно-ориентированная система программного обеспечения будет иметь связи между объектами. Не организация таких связей может привести к ошибкам, которые трудно найти и исправить. Поведенческие шаблоны проектирования преписывают различные методы организации связи между объектами (цепочка ответственности, команда, интерпретатор, итератор, посредник, хранитель, наблюдатель, слуга, спецификация, состояние, стратегия, шаблонный метод, посетитель и т.д.). В этом раздел, мы рассмотрим Наблюдатель (Observer) и Посредник (Mediator) паттерны.
Шаблон Наблюдатель (Observer)
Вот что говорится о Наблюдателе:
В паттерне наблюдатель, объект может иметь список наблюдателей, которые заинтересованы в его жизненном цикле. В любой момент, когда объект делает что-то интересное, он посылает уведомление своим наблюдателям. Если наблюдатель больше не заинтересован в прослушивание объекта, тогда он может удалить его из своего списка.
Нам нужно три метода для описания этого паттерна:
publish(data): вызывается объектом, когда у него есть уведомление. Некоторые данные могут быть переданы с помощью этого метода.
subscribe(observer): вызывается объектом, для добавления наблюдателя в свой список наблюдателей.
unsubscribe(observer): вызывается объектом, чтобы удалить наблюдателя из списка наблюдателей.
Большинство современных JavaScript библиотек поддерживают эти три метода, как часть своей инфраструктуры событий. Обычно есть on() или attach() метод, trigger() или fire() метод, и off() или detach() метод. Рассмотрим следующий сниппет:
//Мы просто создаем связь между методами jQuery событий var o = $( {} ); $.subscribe = o.on.bind(o); $.unsubscribe = o.off.bind(o); $.publish = o.trigger.bind(o); // Usage document.on( 'tweetsReceived', function(tweets) { //perform some actions, then fire an event $.publish('tweetsShow', tweets); }); //Мы можем subscribe к этому событию, а затем fire наше собственное событие. $.subscribe( 'tweetsShow', function() { //display the tweets somehow .. //publish после того, как оно показано $.publish('tweetsDisplayed); }); $.subscribe('tweetsDisplayed, function() { ... });
Паттерн наблюдатель является одним из самых простых шаблонов по реализации, и очень мощным. JavaScript хорошо подходит для принятия этого шаблона, поскольку он сам по себе основан на событиях. В следующий раз при разработки веб-приложений, задумайтесь о разработке модулей, которые слабо связанны друг с другом и принимают паттерн Наблюдатель в качестве средства связи. Паттерн наблюдатель может стать проблематичным, если участвуют слишком много предметов и наблюдателей .
Шаблон Посредник (Mediator)
Последний паттерн, который мы рассмотрим, это Посредник. Он похож на паттерн Наблюдатель, но с некоторыми заметными отличиями.
Посредник, способствует использованию одного общего предмета, который устанавливает связь с несколькими объектами. Все объекты взаимодействуют друг с другом через посредника.
В мире разработки программного обеспечения, шаблон посредник используется, когда система становится слишком сложной. Размещая посредников, связь может осуществляться через единый объект, вместо нескольких объектов, которые общаются друг с другом. В этом смысле, посредник может быть использован для замены системы, которая реализует шаблоны наблюдателя.
Давайте поговорим о том, как можно его использовать. Представьте себе, что у вас есть веб-приложение, которое позволяет пользователям нажать на альбом, и играть музыку из него. Вы можете создать посредника вроде этого:
$('#album').on('click', function(e) { e.preventDefault(); var albumId = $(this).id(); mediator.publish("playAlbum", albumId); }); var playAlbum = function(id) { … mediator.publish("albumStartedPlaying", {songList: [..], currentSong: "Without You"}); }; var logAlbumPlayed = function(id) { //Логин альбом на бенэнде }; var updateUserInterface = function(album) { //Апдейт UI для отображения того, что играет }; //Посредник subscriptions mediator.subscribe("playAlbum", playAlbum); mediator.subscribe("playAlbum", logAlbumPlayed); mediator.subscribe("albumStartedPlaying", updateUserInterface);
Преимущество этого шаблона является в том, что один объект отвечает за связь, в то время как в шаблоне наблюдатель, несколько объектов могут прослушиваться и подписываться на друг друга.
В паттерне наблюдатель, нет ни одного объекта, который заключает в себе ограничения. Вместо этого, наблюдатель и субъект должны сотрудничать для поддержания ограничения. Коммуникационные шаблоны определяются тем, какими путями наблюдатели и субъекты взаимосвязаны: у одного субъекта обычно много наблюдателей, а иногда наблюдатель одного субъекта является предметом другого наблюдателя.
Заключение
Самое замечательное в шаблонах проектирования то, что кто-то уже успешно применил их в прошлом. Существует много open-source кода, который реализует различные шаблоны в JavaScript. Как разработчики, мы должны быть в курсе, какие паттерны есть, и когда их нужно применять.
Перевод (источник)
Источник фото – Fotolia.ru