Паттерны для обработки событий в Javascript. Для дополнительного чтения

В этой статье мы расскажем об общих шаблонах проектирования в 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 = $("This is a div."); //myDiv теперь представляет jQuery объект ссылающейся на DOM узел. var someText = $("

    "); //someText это jQuery объект ссылающейся на HTMLParagraphElement var 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

    Сегодня мы собираемся окунуться в компьютерную науку, так как узнаем о некоторых общих шаблонах проектирования. Шаблоны проектирования предлагают разработчикам способы решения технических проблем в многоразовом и элегантном стиле. Хотите стать лучшим разработчиком JavaScript? Тогда читайте дальше.

    Переизданный учебник

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

    Введение

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

    Что такое шаблон проектирования?

    Шаблон проектирования является многоразовым программным решением

    Проще говоря, шаблон проектирования является многоразовым программным решением для определенного типа проблем, который часто возникает при разработке программного обеспечения. В течение многих лет практики разработки программного обеспечения эксперты выяснили способы решения подобных проблем. Эти решения были инкапсулированы в шаблоны проектирования. Так:

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

    Мы рассмотрим некоторые примеры шаблонов проектирования в учебнике.

    Типы шаблонов проектирования

    При разработке программного обеспечения шаблоны проектирования обычно группируются по нескольким категориям. Мы рассмотрим три наиболее важных из этих уроков. Они поясняются ниже:

  • Порождающие шаблоны сосредоточены на способах создания объектов или классов. Это может показаться простым (и это в некоторых случаях), но большие приложения должны контролировать процесс создания объекта.
  • Структурные шаблоны проектирования фокусируются на способах управления отношениями между объектами, чтобы ваше приложение было сконструировано масштабируемым образом. Ключевым аспектом структурных шаблонов является обеспечение того, чтобы изменение одной части вашего приложения не затрагивало всех других частей.
  • Поведенческие шаблоны фокусируются на связи между объектами.
  • У вас могут возникнуть вопросы после прочтения этих кратких описаний. Это естественно, и все будет проясняться, как только мы рассмотрим некоторые шаблоны проектирования более детально. Так что читайте дальше!

    Заметка о классах в JavaScript

    Когда вы читаете о шаблонах проектирования, вы часто увидите ссылки на классы и объекты. Это может сбивать с толку, поскольку у JavaScript действительно нет конструкции класса; более правильным термином является «тип данных».

    Типы данных в JavaScript

    JavaScript - это объектно-ориентированный язык, где объекты наследуются от других объектов в концепции, известной как прототипное наследование. Тип данных может быть создан путем определения функции, называемой конструктором, например:

    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() { //Private variables var RAM, addRAM; RAM = 4; //Private method addRAM = function (additionalRAM) { RAM += additionalRAM; }; return { //Public variables and methods 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

    Есть намного больше, что мы можем делать с функциями и закрытиями в JavaScript, но мы не будем вникать во все это в этом учебнике. Используя этот маленький урок по типам данных JavaScript и конфиденциальности позади нас, мы можем продолжить изучение шаблонов проектирования.

    Порождающие шаблоны проектирования

    Существует много разных типов шаблонов создания, но мы рассмотрим два из них в этом уроке: Builder и Prototype. Я считаю, что они используются достаточно часто, чтобы уделить им внимание.

    Шаблон Builder

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

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

    Например, вы, вероятно, сделали это бесчисленное количество раз в jQuery:

    Var myDiv = $("This is a div."); //myDiv now represents a jQuery object referencing a DOM node. var someText = $("

    "); //someText is a jQuery object referencing an HTMLParagraphElement var input = $("");

    Взгляните на три примера выше. В первом мы передали элемент с некоторым контентом. Во втором мы передали пустой тег

    В последнем мы передали элемент . Результат всех трех был одинаковым: нам был возвращен объект jQuery, ссылающийся на узел DOM.

    Переменная $ реализует шаблон Builder в jQuery. В каждом примере нам был возвращен объект JQuery DOM и он имел доступ ко всем методам, предоставляемым библиотекой jQuery, но ни в коем случае мы явно не вызывали document.createElement . Библиотека JS обрабатывала все это под капотом.

    Представьте, сколько было бы работы, если бы мы должны были явно создать элемент DOM и вставлять в него контент! Используя шаблон построителя, мы можем сосредоточиться на типе и содержании объекта, а не на его явном создании.

    Шаблон прототипа

    Ранее мы рассмотрели, как определить типы данных в JavaScript через функции и добавить методы к prototype объекта. Шаблон Prototype позволяет объектам наследовать от других объектов через их прототипы.

    Шаблон прототипа - это шаблон, в котором объекты создаются на основе шаблона существующего объекта путем клонирования.

    Это простой и естественный способ реализации наследования в JavaScript. Например:

    Var Person = { numFeet: 2, numHeads: 1, numHands:2 }; //Object.create takes its first argument and applies it to the prototype of your new object. var tilo = Object.create(Person); console.log(tilo.numHeads); //outputs 1 tilo.numHeads = 2; console.log(tilo.numHeads) //outputs 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();

    Единственным недостатком этого метода является то, что вы не можете указывать свойства только для чтения, которые могут быть указаны при использовании Object.create() . Тем не менее, образец прототипа показывает, как объекты могут наследоваться от других объектов.

    Структурные шаблоны проектирования

    Структурные шаблоны проектирования действительно полезны при определении того, как система должна работать. Они позволяют нашим приложениям легко масштабироваться и оставаться обслуживаемыми. Мы рассмотрим следующие шаблоны в этой группе: Composite и Facade.

    Композитный шаблон

    composite шаблон - это еще один шаблон, который вы, вероятно, использовали раньше, без какой-либо реализации.

    В шаблоне composite говорится, что группу объектов можно обрабатывать так же, как отдельный объект группы.

    Так что это значит? Ну, рассмотрим этот пример в jQuery (у большинства JS-библиотек будет эквивалент этого):

    $(".myList").addClass("selected"); $("#myItem").addClass("selected"); //dont do this on large tables, it"s just an example. $("#dataTable tbody tr").on("click", function(event){ alert($(this).text()); }); $("#myButton").on("click", function(event) { alert("Clicked."); });

    Большинство библиотек JavaScript предоставляют согласованный API независимо от того, имеем ли мы дело с одним элементом DOM или с массивом элементов DOM. В первом примере мы можем добавить класс select ко всем элементам, выбранным селектором.myList , но мы можем использовать тот же метод при работе с единственным элементом DOM, #myItem . Аналогично, мы можем присоединить обработчики событий, используя метод on() на нескольких узлах или на одном узле через один и тот же API.

    Используя шаблон Composite, jQuery (и многие другие библиотеки) предоставляют нам упрощенный API.

    Шаблон composite иногда может вызывать проблемы. В свободно типизированном языке, таком как JavaScript, часто бывает полезно знать, имеем ли мы дело с одним элементом или несколькими элементами. Поскольку шаблон composite использует один и тот же API для обоих случаев, мы иногда можем ошибаться и принимать один объект за несколько и наоборот. Некоторые библиотеки, такие как YUI3, предлагают два отдельных метода получения элементов (Y.one() против Y.all()).

    Фасад

    Вот еще одна общая картина, которую мы считаем само собой разумеющейся. На самом деле, это один из моих любимых, потому что он простой, и я видел, как он используется повсюду, чтобы помочь нам с несогласованностью браузеров. Вот что такое шаблон Facade:

    Шаблон Facade предоставляет пользователю простой интерфейс, скрывая его сложность.

    Шаблон Facade почти всегда улучшает удобство использования программного обеспечения. Используя jQuery в качестве примера снова, одним из наиболее популярных методов библиотеки является метод ready() :

    $(document).ready(function() { //all your code goes here... });

    Метод ready() фактически реализует фасад. Если вы посмотрите на исходный код, вот что вы найдете:

    Ready: (function() { ... //Mozilla, Opera, and Webkit if (document.addEventListener) { document.addEventListener("DOMContentLoaded", idempotent_fn, false); ... } //IE event model else if (document.attachEvent) { // ensure firing before onload; maybe late but safe also for iframes document.attachEvent("onreadystatechange", idempotent_fn); // A fallback to window.onload, that will always work 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() . Рассмотрим следующий фрагмент:

    //We just create an association between the jQuery events methods //and those prescribed by the Observer Pattern but you don"t have to. 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); }); //We can subscribe to this event and then fire our own event. $.subscribe("tweetsShow", function() { //display the tweets somehow .. //publish an action after they are shown. $.publish("tweetsDisplayed); }); $.subscribe("tweetsDisplayed, function() { ... });

    Шаблон Observer - один из самых простых моделей для реализации, но он очень мощный. JavaScript хорошо подходит для принятия этого шаблона, поскольку он, естественно, основан на событиях. В следующий раз, когда вы разрабатываете веб-приложения, подумайте о разработке модулей, которые слабо связаны друг с другом и используйте шаблон Observer в качестве средства коммуникации. Образец наблюдателя может стать проблематичным, если слишком много субъектов и наблюдателей. Это может произойти в крупномасштабных системах, и следующий шаблон, который мы рассмотрим, пытается решить эту проблему.

    Посредник

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

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

    Хорошей аналогией в реальном мире будет вышка в аэропорте, которая обрабатывает связь между аэропортом и рейсами. В мире разработки программного обеспечения шаблон 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) { //Log the album in the backend }; var updateUserInterface = function(album) { //Update UI to reflect what"s being played }; //Mediator subscriptions mediator.subscribe("playAlbum", playAlbum); mediator.subscribe("playAlbum", logAlbumPlayed); mediator.subscribe("albumStartedPlaying", updateUserInterface);

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

    В шаблоне Observer нет единого объекта, который инкапсулирует ограничение. Вместо этого наблюдатель и субъект должны сотрудничать для поддержания ограничения. Шаблоны комуникации определяются тем, как наблюдатели и субъекты взаимосвязаны: у одного субъекта обычно есть много наблюдателей, и иногда наблюдатель одного субъекта является субъектом другого наблюдателя.

    Заключение

    Кто-то уже успешно применял это в прошлом.

    Самое замечательное в шаблонах проектирования - то, что кто-то уже успешно применял его в прошлом. Существует множество открытых исходных кодов, которые реализуют различные шаблоны в JavaScript. Как разработчикам, нам нужно знать, какие шаблоны есть и когда их применять. Я надеюсь, что этот урок помог вам сделать еще один шаг к ответу на эти вопросы.

    Для дополнительного чтения

    Большая часть контента из этой статьи находится в отличной книге « » Адди Османи. Это онлайн-книга, которая была выпущена бесплатно под лицензией Creative Commons. Книга широко охватывает теорию и реализацию множества различных шаблонов, как в ванильном JavaScript, так и в различных JS-библиотеках. Я призываю вас заглянуть в нее, когда вы начнете свой следующий проект.

    Возможно, вы уже знакомы с такими фреймворками/библиотеками как ReactJS, Angular или jQuery, но знаете ли вы, зачем они были созданы? Какую проблему решают? Какой шаблон проектирования используют? Шаблоны проектирования очень важно знать, и, могу предположить, некоторые из разработчиков-самоучек могли их пропустить. Так что же такое шаблоны проектирования? Что они делают? Как мы можем их использовать? Почему их следует использовать?

    Что такое шаблоны проектирования и зачем их использовать? В разработке программного обеспечения, шаблон проектирования является основным многоразовым решением общей проблемы в рамках данного контекста. Это не готовый макет, который может быть трансформирован непосредственно в источник или машинный код. Это описание или шаблон того, как решить проблему, которая может возникнуть в различных ситуациях. Шаблоны проектирования - это принявшие устойчивую форму лучшие практики, которые программист может использовать для решения общих проблем при разработке приложения или системы.

    Шаблоны проектирования - проверенный способ для решения проблем. Они не включают в себя такие очевидные вещи, как использование for loop для перебора элементов массива. Их используют для решения более сложных проблем, с которыми мы сталкиваемся при разработке больших приложений.

    Некоторые из плюсов использования шаблонов проектирования:

    • Не нужно изобретать велосипед (ленивый программист => хороший программист)
    • Находишь общий язык с разработчиками
    • Выглядишь круто и профессионально
    • Если ты самоучка, то это поможет тебе изрядно выделиться среди конкурентов
    • Эти шаблоны проходят сквозь все языки программирования
    Типы шаблонов и примеры некоторых из них

    Порождающие шаблоны (Creational): создание новых объектов.

  • Конструктор (Constructor)
  • Модульный (Module)
  • Фабричный метод (Factory)
  • Одиночка (Singletion)
  • Структурные шаблоны(Structural): упорядочивают объекты.

  • Декоратор(Decorator)
  • Фасад (Façade)
  • Поведенческие (Behavioral): как объекты соотносятся друг с другом.

  • Наблюдатель(Observer)
  • Посредник(Mediator)
  • Команда(Command)
  • Порождающее шаблоны

    Эти шаблоны используются для создания новых объектов.

    Constructor

    Создает новые объекты в их собственной области видимости.

    // Constructor Pattern //
    // Используйте для создания новых объектов в их собственной области видимости. var Person = function(name, age, favFood) {
    this.name = name;
    this.age = age;
    this.favFood = favFood;
    }; // Прототип позволяет всем экземплярам Person ссылаться на него без повторения функции.
    Person.prototype.greet = function() {
    console.log(`Hello, my name is ${this.name}, I"m ${this.age} years old, and my favorite food is ${this.favFood}`); // new создает объект {} и передает "this" в конструктор
    // Конструктор устанавливает значение для этого объекта и возвращает его.
    var bob = new Person("Bob", 22, "Chicken");
    bob.greet();
    // Hello, my name is Bob, I"m 22 years old, and my favorite food is Chicken // ES6 / ES2015 Классы
    class Vehicle {
    constructor(type, color) {
    this.type = type;
    this.color = color;
    } getSpecs() {
    console.log(`Type: ${this.type}, Color: ${this.color}`);
    }
    }; var someTruck = new Vehicle("Truck", "red");
    someTruck.getSpecs();

    Module

    Используйте для инкапсуляции методов.

    // Module Pattern //
    // Используется для инкапсуляции кода var myModule = (function() {
    // Приватная переменная
    var memes = ["cats", "doge", "harambe"];

    Var getMemes = function() {
    return memes;
    }; // возвращает то, к чему вы хотите разрешить доступ в объекте
    // то, как он это возвращает действительно делает его показателем модульного шаблона проектирования return {
    getMemes: getMemes
    };
    })(); console.log(myModule.getMemes()); // массив мемов
    console.log(myModule.memes); // undefined

    Factory

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

    // Factory Pattern //
    // Несколько конструкторов для нашей фабрики function Cat(options) {
    this.sound = "Meow";
    this.name = options.name;
    } function Dog(options) {
    this.sound = "Rawr";
    this.name = options.name;
    } // Animal Factory
    function AnimalFactory() {} // Тип Cat по умолчанию
    AnimalFactory.prototype.animalType = Cat; // метод для создания новых животных
    AnimalFactory.prototype.createAnimal = function(options) {
    switch(options.animalType) {
    case "cat":
    this.animalType = Cat;
    break;
    case "dog":
    this.animalType = Dog;
    break;
    default:
    this.animalType = Cat;
    break;
    } return new this.animalType(options);
    } var animalFactory = new AnimalFactory();
    var doge = animalFactory.createAnimal({
    animalType: "dog",
    name: "Doge"
    }); var snowball = animalFactory.createAnimal({name: "Snowball"}); console.log(doge instanceof Dog); // true
    console.log(doge); // выводит doge как cat объект
    console.log(snowball instanceof Cat); // true
    console.log(snowball); // выводит snowball как cat объект

    Singleton

    Используйте для того, чтобы ограничиться одним экземпляром объекта.

    Несмотря на то, что Одиночка действительно имеет применение, обычно, если мы замечаем, что он необходим нам в JavaScript, это признак того, что следует переоценить наш дизайн. Обычно это свидетельствует о том, что модули в системе либо тесно связаны, либо, что эта логика чрезмерно распространена по всем частям кода. Одиночку сложнее тестировать из-за проблем связанных с скрытыми зависимостями, сложностью с созданием нескольких экземпляров, трудностями в установлении зависимостей и т.д. Структурные шаблоны

    Как создаются объекты и какие взаимоотношения между ними. Расширяет или упрощает функциональность.

    Decorator

    Используйте, чтоб добавлять новую функциональность объектам (Расширяет функциональность ).

    // Decorator Pattern //
    // Простой конструктор var Person = function (name) {
    this.name = name;
    } Person.prototype.greet = function () {
    console.log(`Hello, my name is ${this.name}`);
    } var uniqueBob = new Person("Bob"); // может быть добавлено к нему без изменения конструктора Person
    uniqueBob.hobbies = ["Cooking", "Running"]; uniqueBob.greet = function() {
    console.log("My hobbies are: ", this.hobbies);
    }; uniqueBob.greet(); // Другой способ
    var CoolPerson = function(name, catchPhrase) {
    Person.call(this, name);
    this.catchPhrase = catchPhrase;
    }; // включает в себя prototypes от Person
    CoolPerson.prototype = Object.create(Person.prototype); // изменяет прототип
    CoolPerson.prototype.greet = function() {
    Person.prototype.greet.call(this);
    console.log(this.catchPhrase);
    }; var coolDude = new CoolPerson("Jeff", "Aaaayyy");
    console.log(coolDude);
    coolDude.greet();

    Facade

    Используйте для создания простого интерфейса (упрощает функциональность , как например jQuery).

    // Facade Pattern //
    // абстрагирует он некоторых сложных/неряшлевых вещей var $ = function (target) {
    return new MemeQuery(target);
    }; function MemeQuery (target) {
    this.target = document.querySelector(target);
    } MemeQuery.prototype.html = function(html) {
    this.target.innerHTML = html;
    return this;
    }; // теперь, все, что мы будем видеть и использовать это $
    $("#myParagraph").html("Meeemee").html("Some JS design patterns"); //окей, возможно это и не лучший пример..
    // просто посмотрите в исходный код jQuery, там полно примеров фасада
    // он нужен просто для того, чтоб увести нас от того, чтоб заострять внимание на сложностях проектирования и сделать проектирование быстрее и проще.

    Поведенческие шаблоны

    Распределяют обязанности между объектами и тем, как они сообщаются.

    Observer

    Позволяет объектам наблюдать за объектами и быть оповещенными об изменениях.

    // Observer Pattern //
    // https://github.com/CodeDraken/emtr/blob/master/emitter.js

    Mediator

    Один объект контролирует сообщение между объектами, поэтому объекты не сообщаются друг с другом на прямую.

    // Mediator Pattern //
    // Извините, я очень устал для того, чтоб придумывать примеры:P

    Command

    Инкапсулирует вызов метода в один объект.

    // Command Pattern //
    // Пример из:
    (function(){
    var carManager = { // Запросить информацию
    requestInfo: function(model, id){
    return "The information for " + model + " with ID " + id + " is foobar";
    },
    // Купить машину
    buyVehicle: function(model, id){
    return "You have successfully purchased Item " + id + ", a " + model;
    }, // Организвать прсмотр
    arrangeViewing: function(model, id){
    return "You have successfully booked a viewing of " + model + " (" + id + ") ";
    }
    };
    })(); carManager.execute = function (name) {
    return carManager && carManager.apply(carManager, .slice.call(arguments, 1));
    }; carManager.execute("arrangeViewing", "Ferrari", "14523");
    carManager.execute("requestInfo", "Ford Mondeo", "54323");

    Фуух, сколько этих шаблонов… Я надеюсь, вы еще здесь, потому что у нас осталось еще 22 шаблона для рассмотрения. Шучу, это все! Но есть еще шаблоны, которые следует изучить, поэтому я добавлю ссылки для тех, кто хочет узнать об этом побольше.

    Some Design Patterns in JavaScript

    P.S. Это мой первый перевод для medium. Спасибо всем, что прочитали! Надеюсь, вы действительно узнали что-то новое. Буду рада услышать ваши комментарии!

    Последние несколько месяцев в сети шли бурные дебаты о наиболее удачных способах обработки событий. Сначала Google выпустил библиотеку JsAction, а затем был анонсирован метод Object.observe() , который будет реализован в стандарте ECMAScript 7, но уже поддерживается в Chrome 36 и Node.js Harmony .

    Разработчики разделились во мнениях: обязательно ли сохранять всю логику в файлах JS или же допустимо вставлять отдельные части логики в HTML. В этой статье мы попробуем разобраться в этих спорах, рассмотрев различные паттерны для обработки ошибок и затем взвесим за и против у всех альтернатив.

    Факты

    JsAction это библиотека Google для делегирования событий в JS. Она основана на библиотеке Closure и впервые была применена в Google-картах пару лет назад для решения проблем с ошибками обработчиков событий в некоторых браузерах. JsAction нацелена на отделение событий от методов их обработки и с этой целью переносит часть логики обработчиков событий в HTML.

    Вообще, это один из трендов в веб-разработке - переносить часть логики не просто в HTML, а внутрь DOM-элементов, затрагиваемых логикой. И это относится не только к обработке событий, есть основанные на шаблонах фреймворки (Angular, Ractive, React), они реализуют паттерн Модель-Представление-Контроллер в веб-приложениях, позволяя связывание данных и реактивное программирование.

    Введение метода Object.observe() в следующей спецификации ECMAScript это еще один шаг в этом направлении, так как дает разработчикам нативную реализацию паттерна Издатель/Подписчик, который применим во многих ситуациях, не только при обработке событий. Декларативные фреймворки уже основаны на таком подходе, а реализация Object.observe() позволит им достигнуть большей производительности.

    Исторический экскурс

    С самого начала применения JavaScript подход к обработке событий неоднократно менялся. Первоначально, для добавления динамических элементов на страницу был только один способ - добавление атрибута к нужному тэгу и подключение к нему фрагмента JavaScript-кода. Можно было написать код непосредственно внутри атрибута или же вызвать одну из функций определенных в глобальном пространстве.

    Вот как выглядело изменение фонового цвета страницы по нажатию мышки в те времена:

    Feel Blue

    Прошло немного времени до того как ограничения и опасности использования JS в атрибутах HTML-тегов были выявлены. В ноябре 2000 года в стандарт ECMAScript 3 был включен новый способ привязывания обработчиков к событиям браузера. К этому моменту компания Microsoft уже реализовала в своем браузере метод attachEvent() , но прошло время, прежде чем эти методы начали использоваться. Только спустя 4 года после первого упоминания (разработчиками Netscape Navigator) концепция ненавязчивого JS начала распространяться.

    Разработчики в Netscape Navigator считали, что прослушивание событий устраняют следующие недостатки инлайновых обработчиков событий:

    • Смешивание кода и разметки делает код менее читаемым и затрудняет его поддержку.
    • Загрязнение глобального пространства имен: весь инлайновый код определяется в глобальном пространстве, соответственно, каждая функция обращающаяся к этим обработчикам также должна быть глобальной.
    • Это мишень для XSS-атак, в атрибуте может быть любой код, в том числе вредоносный, который можно передать в eval без всякой проверки.

    В 2006 году, с выпуском первых библиотек, ориентированных на широкое использование Ajax, таких как YUI и Jquery, подход с использованием прослушивания событий был применен без всякого ожидания и это позволило ускорить внедрение передовых практик, что в итоге к ним пришло большинство разработчиков.

    Тогда же к методу прослушивания событий были добавлены:

    • Масштабируемость: инкапсуляция обработчика событий внутри функции соответствует принципу DRY, так как позволяет работать на уровне прототипа объекта и использовать одинаковую логику для множественных обработчиков; а с помощью механизма селекторов JQuery CSS дает легкий и эффективный способ привязывания обработчиков к набору узлов:
    $(document).ready(function () { $(".clickable").click(function () { document.body.style.background="lightblue"; return false; }); });
    • Отладка: с использованием таких инструментов как Firebug и Chrome Developer Tools отладка JS становится меньшим кошмаром, по сравнению с отладкой инлайнового кода.
    Проблемы с паттерном addEventListener

    Подход с прослушиванием событий, несмотря на свои очевидные плюсы имеет и недостатки:

    • Прикрепление слушателей к объектам может вести к утечкам памяти в замыканиях, если сделано с ошибкой. Замыкания это одна из наиболее мощных возможностей JS, но использовать их надо с осторожностью, когда они переплетаются с DOM-элементами. В замыканиях имеется указатель на их область видимости. В результате, прикрепление замыкания к DOM-элементу может вызвать круговое обращение к нему и, соответственно, утечку памяти. Вот пример из руководства Google по стилю написания JavaScript , с образцами правильной и неправильной реализаций.
    • В ИЕ также возникает проблема с уборкой мусора, особенно, когда дело доходит до событий. Кроме общеизвестной проблемы взаимных циклических ссылок, старые версии ИЕ не удаляю обработчик после удаления элемента DOM, что влечет дополнительные утечки.
    Еще раз: что такое JsAction?

    Вот мы и подошли к JsAction. Как упоминалось в начале поста, это библиотека делегирования событий, которая позволяет связывать события и обработчики посредством их имен, используя специальный HTML атрибут jsaction , который будет обработан библиотекой.

    Каждый обработчик событий отдельно регистрируется в одном или нескольких JS файлах или скриптах; они ассоциированы с названиями методов, а так как связь между именами и функциями берет на себя библиотека, нет необходимости добавлять их в глобальный контекст.

    В целом, использование JsAction должно дать следующие выигрыши:

  • Решение проблемы утечки памяти в старых браузерах.
  • Предотвращение или уменьшение загрязнение глобального пространства имен.
  • Уменьшение зависимости обработки событий от конкретной реализации в браузере.
  • Лучшая производительность и масштабируемость, позволяющая установить один прослушиватель событий на странице и затем через него маршрутизировать события к их обработчикам.
  • Образец работы JsAction можно увидеть на странице библиотеки на GitHub.

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

    Реализация пунктов 3 и 4 более сложна: нам необходимо установить единственный обработчик для всей страницы, установить атрибут в элементах DOM, установив, какой метод мы будем использовать в качестве обработчика и создать “супер-обработчик”, который будет маршрутизировать события к обработчикам.

    Еще раз отмечу, не обязательно, что это будет правильным решением именно для ваших задач, все зависит от характеристик вашего проекта. Несмотря на обилие плюсов, у него есть отдельные недостатки:

  • Библиотека не относиться к легковесным.
  • Она не выглядит особо интуитивной в использовании, и кривая обучения может быть достаточно крутой для начинающих. Документация по ней на данный момент явно не достаточная.
  • Начало работы с ней может быть достаточно трудным. В отсутствие готовой скомпилированной версии необходимо в начале скачать библиотеку Closure с компилятором и скомпилировать JsAction.
  • Декларативные фреймворки

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

    Подождите, но ведь смешивание логики приложения и представления это плохо? Да. Мы рассмотрели некоторые преимущества отделения логики от представления, такие как легкость отладки и ясность кода. Но временами поддержка проекта может быть улучшена указанием применяемой к объекту логики непосредственно в самом объекте.

    Такие фреймворки как RactiveJs , Angular , Ember , and React не просто позволяют встраивать код в представления. Они активно используют модели, основанные на шаблонах, что позволяет связывать обработчики событий, данные и логику представления внутри элементов DOM, а затем специализировать эту логику в различных скриптах. В принципе, та же схема используется в JsAction для разделения имен обработчиков событий и их имплементации. В итоге они скорее увеличивают разделение между логикой и представлением, выводя приложения на основе MVC на более высокий уровень и параллельно упрощая использование шаблонов.

    Эти фреймворки позволяют делать намного больше, чем простая обработка событий. Они дают связывание данных, что необходимо для успешной реализации разделения MVC. Привязывание элементов представления к объектам JS позволяет мгновенно обновлять представления при изменении объектов. Кром того, обновление происходит особо эффективно, так как изменяется только минимальное число элементов DOM, что сокращает перерисовку страницы, которая обычно является главным фактором, снижающим скорость на веб-страницах.

    С этой же целью, Ractive и React используют виртуальный DOM - абстрактное представление DOM, которое позволяет проводить очень быстрые операции за счет сокращения необходимых манипуляций с DOM. Они очень похожи между собой, фокусируясь на реактивном программировании и визуализации. В отличие от Angular, который не фокусируется на представлениях, а является более комплексным фреймворком, позволяя обрабатывать маршрутизацию, соединение с сервером и т.д.

    Все эти фреймворки поддерживают двухстороннее связывание, удобный способ для достижения согласованности между состоянием DOM элементов и логики приложения. Например, если вам нужно вывести на экран список чего-либо. В традиционной императивной парадигме вам нужен примерно такой код:

    function createItemHTML (val) { return "" + val + ""; } function displayList (container, items) { container.empty(); $.each(items, function (index, val) { var element = $(""); element.attr("id", "div_" + index); element.html(createItemHTML(val)); container.append(element); }); } function editItem (container, itemId, itemValue) { var element = container.find("#" + itemId); if (element) { element.html(createItemHTML(itemValue)); } } //... displayList($("#container"), items); //... editItem(container, id, newVal);

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

    А вот как выглядит решение той же задачи средствами Ractive:

    {#items:num} {/items} var ractive = new Ractive({ el: "container", template: "#listTemplate", data: { "items": items } }); ractive.on({ "itemClick": function (e) { //access e.node and e.context for both the DOM element // and the Ractive state associated with it } }); //... //Now update items with a new list ractive.set("items", newItemsList);

    Все! Нет необходимости писать код для обновления страницы. Ractive сделает это за вас. Она яснее, поддерживаемее, лучше спроектирована и более производительна. Мы также можем добавлять обработчики событий на лету.

    Object.observe()

    Object.observe() это взгляд в будущее, потому как его даже нет в 6-ой спецификации EcmaSript, он есть только в новейшей 7-ой версии. Однако компания Гугл уже почти реализовала его в последней версии своего браузера (Chrome 36), а библиотека Polymer Observe-JS предлагает полифилл для этого метода в современных браузерах, до тех пор, пока в них не появится нативная поддержка.

    Этот метод позволяет асинхронно наблюдать изменения в объектах и массивах. Наблюдатели будут получать своевременные данные об изменениях, произошедших в обозреваемых объектах. С Object.observe() событийно-ориентированное программирование (чаще называемое реактивным) выходит за рамки разработки пользовательских интерфейсов. Например, можно будет реализовать двухстороннее связывание на базе JS, без необходимости устанавливать для этих целей фреймворк типа Ractive.

    Связывание данных в декларативных фреймворках

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

    Другой способ, применяемый в Ember, Backbone и Ractive это использование объекта-контейнера. фреймворк создает объекты в которых хранятся данные. В этих объектах есть методы доступа к данным, и каждый раз при запросе или изменении данных, фреймворк захватывает ваше действие и распространяет сведения о нем подписчикам. Это решение отлично работает, более производительно, чем “грязный контроль” и лучше масштабируется.

    Улучшение производительности

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

    // A model can be an object literal var plainObject = { name: "Counter", total: 0 }; // Define an observer method function observer(changes){ changes.forEach(function(change, i){ console.log("what property changed? " + change.name); console.log("how did it change? " + change.type); console.log("whats the current value? " + change.object); console.log(change); // all changes }); } // Start watching the object Object.observe(plainObject, observer);

    Прекращение наблюдения за объектом делается в одну строчку кода.

    Object.unobserve(plainObject, observer);

    План развития

    Как я уже написал, нативная поддержка Object.observe() реализована в Chrome 36 и в Node.js с включенным флагом harmony. Также реализация предусмотрена в ближайших версиях Opera. В остальных браузерах можно пока использовать полифилл на Observe-JS Polymer library, что также дает поддержку некоторых старых браузеров.

    В декларативных фреймворках также ожидается поддержка этого метода - в Ember и Ractive она запланирована в ближайших релизах, в Angular с выходом мажорного релиза.

    Заключение

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

    This UML describes how a prototype interface is used to clone concrete implementations.

    To clone an object, a constructor must exist to instantiate the first object. Next, by using the keyword prototype variables and methods bind to the object"s structure. Let"s look at a basic example:

    Var TeslaModelS = function() { this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; } TeslaModelS.prototype.go = function() { // Rotate wheels } TeslaModelS.prototype.stop = function() { // Apply brake pads }

    The constructor allows the creation of a single TeslaModelS object. When a creating new TeslaModelS object, it will retain the states initialized in the constructor. Additionally, maintaining the function go and stop is easy since we declared them with prototype . A synonymous way to extend functions on the prototype as described below:

    Var TeslaModelS = function() { this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; } TeslaModelS.prototype = { go: function() { // Rotate wheels }, stop: function() { // Apply brake pads } }

    Revealing Prototype Pattern

    Similar to Module pattern, the Prototype pattern also has a revealing variation. The Revealing Prototype Pattern provides encapsulation with public and private members since it returns an object literal.

    Since we are returning an object, we will prefix the prototype object with a function . By extending our example above, we can choose what we want to expose in the current prototype to preserve their access levels:

    Var TeslaModelS = function() { this.numWheels = 4; this.manufacturer = "Tesla"; this.make = "Model S"; } TeslaModelS.prototype = function() { var go = function() { // Rotate wheels }; var stop = function() { // Apply brake pads }; return { pressBrakePedal: stop, pressGasPedal: go } }();

    Note how the functions stop and go will be shielded from the returning object due to being outside of returned object"s scope. Since JavaScript natively supports prototypical inheritance, there is no need to rewrite underlying features.

    There are many times when one part of the application changes, other parts needs to be updated. In AngularJS, if the $scope object updates, an event can be triggered to notify another component. The observer pattern incorporates just that - if an object is modified it broadcasts to dependent objects that a change has occurred.

    Another prime example is the model-view-controller (MVC) architecture; The view updates when the model changes. One benefit is decoupling the view from the model to reduce dependencies.

    Wikipedia

    As shown in the UML diagram, the necessary objects are the subject , observer , and concrete objects. The subject contains references to the concrete observers to notify for any changes. The Observer object is an abstract class that allows for the concrete observers to implements the notify method.

    Let"s take a look at an AngularJS example that encompasses the observer pattern through event management.

    // Controller 1 $scope.$on("nameChanged", function(event, args) { $scope.name = args.name; }); ... // Controller 2 $scope.userNameChanged = function(name) { $scope.$emit("nameChanged", {name: name}); };

    With the observer pattern, it is important to distinguish the independent object or the subject .

    It is important to note that although the observer pattern does offer many advantages, one of the disadvantages is a significant drop in performance as the number of observers increased. One of the most notorious observers is watchers . In AngularJS, we can watch variables, functions, and objects. The $$digest cycle runs and notifies each of the watchers with the new values whenever a scope object is modified.

    We can create our own Subjects and Observers in JavaScript. Let"s see how this is implemented:

    Var Subject = function() { this.observers = ; return { subscribeObserver: function(observer) { this.observers.push(observer); }, unsubscribeObserver: function(observer) { var index = this.observers.indexOf(observer); if(index > -1) { this.observers.splice(index, 1); } }, notifyObserver: function(observer) { var index = this.observers.indexOf(observer); if(index > -1) { this.observers.notify(index); } }, notifyAllObservers: function() { for(var i = 0; i < this.observers.length; i++){ this.observers[i].notify(i); }; } }; }; var Observer = function() { return { notify: function(index) { console.log("Observer " + index + " is notified!"); } } } var subject = new Subject(); var observer1 = new Observer(); var observer2 = new Observer(); var observer3 = new Observer(); var observer4 = new Observer(); subject.subscribeObserver(observer1); subject.subscribeObserver(observer2); subject.subscribeObserver(observer3); subject.subscribeObserver(observer4); subject.notifyObserver(observer2); // Observer 2 is notified! subject.notifyAllObservers(); // Observer 1 is notified! // Observer 2 is notified! // Observer 3 is notified! // Observer 4 is notified!

    Publish/Subscribe

    The Publish/Subscribe pattern, however, uses a topic/event channel that sits between the objects wishing to receive notifications (subscribers) and the object firing the event (the publisher). This event system allows code to define application-specific events that can pass custom arguments containing values needed by the subscriber. The idea here is to avoid dependencies between the subscriber and publisher.

    This differs from the Observer pattern since any subscriber implementing an appropriate event handler to register for and receive topic notifications broadcast by the publisher.

    Many developers choose to aggregate the publish/subscribe design pattern with the observer though there is a distinction. Subscribers in the publish/subscribe pattern are notified through some messaging medium, but observers are notified by implementing a handler similar to the subject.

    In AngularJS, a subscriber "subscribes" to an event using $on("event", callback), and a publisher "publishes" an event using $emit("event", args) or $broadcast("event", args).

    Singleton

    A Singleton only allows for a single instantiation, but many instances of the same object. The Singleton restricts clients from creating multiple objects, after the first object created, it will return instances of itself.

    Finding use cases for Singletons is difficult for most who have not yet used it prior. One example is using an office printer. If there are ten people in an office, and they all use one printer, ten computers share one printer (instance). By sharing one printer, they share the same resources.

    Var printer = (function () { var printerInstance; function create () { function print() { // underlying printer mechanics } function turnOn() { // warm up // check for paper } return { // public + private states and behaviors print: print, turnOn: turnOn }; } return { getInstance: function() { if(!printerInstance) { printerInstance = create(); } return printerInstance; } }; function Singleton () { if(!printerInstance) { printerInstance = intialize(); } }; })();

    The create method is private because we do not want the client to access this, however, notice that the getInstance method is public. Each officer worker can generate a printer instance by interacting with the getInstance method, like so:

    Var officePrinter = printer.getInstance();

    In AngularJS, Singletons are prevalent, the most notable being services, factories, and providers. Since they maintain state and provides resource accessing, creating two instances defeats the point of a shared service/factory/provider.

    Race conditions occur in multi-threaded applications when more than one thread tries to access the same resource. Singletons are susceptible to race conditions, such that if no instance were initialized first, two threads could then create two objects instead of returning and instance. This defeats the purpose of a singleton. Therefore, developers must be privy to synchronization when implementing singletons in multithreaded applications.

    Conclusion

    Design patterns are frequently used in larger applications, though to understand where one might be advantageous over another, comes with practice.

    Before building any application, you should thoroughly think about each actor and how they interact with one another. After reviewing the Module , Prototype , Observer , and Singleton design patterns, you should be able to identify these patterns and use them in the wild.