JS
Design Patterns
Agenda
- Module
- Singleton
- Observer
- PubSub
- Mediator
- Facade
Design patterns (GOF)
Шаблоны проектирования - это повторяющиеся решения общих проблем в разработке программного обеспечения.
Design patterns (GOF)
-
Порождающие паттерны - предназначены для создания новых объектов в системе.
-
Структурные паттерны - решают задачи компоновки системы на основе классов и объектов (рассматривается вопрос о том, как из классов и объектов образуются более крупные структуры).
-
Паттерны поведения - предназначены для распределения обязанностей между объектами в системе.
Design Patterns Elements of Reusable Object-Oriented Software
Module
«Модуль» — это популярная реализация паттерна, инкапсулирующего приватную информацию, состояние и структуру, используя замыкания. Это позволяет оборачивать публичные и приватные методы и переменные в модули, и предотвращать их попадание в глобальный контекст, где они могут конфликтовать с интерфейсами других разработчиков. Паттерн «модуль» возвращает только публичную часть API, оставляя всё остальное доступным только внутри замыканий.
Module
const counter = {
counter: 0,
incrementCounter: function () {
return ++this.counter;
},
resetCounter: function () {
return this.counter = 0;
}
};
Module
const counterModule = (function () {
// private code
return {
// public interface or methods
}
})();
Module
var counterModule = (function () {
var counter = 0;
return {
incrementCounter: function () {
return ++counter;
},
resetCounter: function () {
return counter = 0;
}
}
})();
Module
var wrapperId = 'counter';
var counterModule2 = (function (id) {
var counter = 0;
console.log(id);
return {
incrementCounter: function () {
return ++this.counter;
},
resetCounter: function () {
return this.counter = 0;
}
}
})('123');
with import
Module
var counterModule3 = (function () {
var counter = 0;
var module = {};
module.incrementCounter = function () {
return ++this.counter;
};
module.resetCounter = function () {
return this.counter = 0;
};
return module;
})();
with export
Module
Это хорошее решение для того, чтобы скрыть внутреннюю логику от посторонних глаз и производить всю тяжелую работу исключительно через интерфейс, который вы определите для использования в других частях вашего приложения. Этот паттерн очень похож на немедленно-вызываемые функции (IIFE), за тем исключением, что модуль вместо функции, возвращает объект.
Bonus Module
var Neuron = (function (jQuery, Underscore) {
var Cell = function () {};
function say(message) {
console.log(message);
}
Cell.prototype = {
migrate: function() {
// cell is going to find a better place
say('travelling');
},
learn: function(subj) {
// trying something new
say('f***ing ' + subj);
},
duplicate: function() {}
};
return Cell;
})($, _);
// Usage:
// Creates new neuron instance
var brainCell = new Neuron(); // Outputs: travelling
brainCell.migrate(); // Outputs: f***ing math
brainCell.learn('math');
Module Constructor
Singleton
Wherefore:
Provide single instance of class. When you try to create another instance, the program should receive an item that has already been created.
How to:
Using module pattern
Singleton
var Singleton = (function () {
var instance;
function createInstance() {
var object = new Object("I am the instance");
return object;
}
return {
getInstance: function () {
// creates instans if it doesn't exist
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// Usage:
// Creates new instance
var instance1 = Singleton.getInstance();
var instance2 = Singleton.getInstance();
// Outputs: true
console.log(instance1 === instance2);
Observer/PubSub
Wherefore:
This is where the objects in a system may subscribe to other objects and be notified by them when an event of interest occurs.
How to:
Using javascript events
Observer
Observer представляет собой не что иное, как связь один-ко-многим. В упрощенном виде этот паттерн состоит из объекта наблюдения (subject) и наблюдателей (observers).
Принципиальная схема взаимодействия выглядит так:
- Subject — реализует методы: observe, detach, notify, get, set.
- Observer — реализует метод update.
Также subject содержит ссылки на всех observers, которые его слушают, а observer, в свою очередь содержит ссылку, на subject, на который он подписан.
Observer
Observer
$('button').on('click', function(){
alert('button clicked');
});
$('button').on('click', function(){
alert('button clicked');
});
Pub-Sub
Pub-sub паттерн является одной из вариаций паттерна Observer. Исходя из названия в паттерне выделяют два компонента Publisher (издатель) и Subscriber (подписчик). В отличие от Observer, связь между объектами осуществляется посредством канала связи Event Channel (шины событий).
Publisher кидает свои события в Event Channel, а Subscriber подписывается на нужное событие и слушает его на шине, что обеспечивает отсутствие прямой связи между подписчиком и издателем.
Observer and Pub-Sub
Observer and Pub-Sub
Таким образом можно выделить основные отличительные особенности между Pub-sub и Observer:
- отсутствие прямой связи между объектами
- объекты сигнализируют друг другу событиями, а не состояниями объекта
- возможность подписываться на различные события на одном объекте с различными обработчиками
Observer and Pub-Sub
(function( $ ) {
var o = $( {} );
$.each({
trigger: 'publish',
on: 'subscribe',
off: 'unsubscribe'
}, function( key, val ) {
jQuery[val] = function() {
o[key].apply( o, arguments );
};
});
})( jQuery );
// $.getJSON('http://search.twitter.com/search.json?q=dogs&callback=?', function( results) {
// $.publish( 'twitter/results', results );
// });
setTimeout(function() {
$.publish( 'twitter/results', { results: [{text: 'test'}] } );
}, 2000);
$.subscribe( 'twitter/results', function( e, results ) {
$('body').html(
$.map( results.results, function( obj, index) {
return '<li>' + obj.text + '</li>';
}).join('')
);
});
Pub-Sub
class PubSub {
constructor() {
this.handlers = [];
}
subscribe(event, handler, context) {
if (typeof context === 'undefined') { context = handler; }
this.handlers.push({ event: event, handler: handler.bind(context) });
}
publish(event, args) {
this.handlers.forEach((topic) => {
if (topic.event === event) {
topic.handler(args)
}
})
}
}
class ChatRoom {
constructor() {
this.pubsub = new PubSub();
this.pubsub.subscribe('event-message', this.emitMessage, this);
}
emitMessage(msg) {
console.group('PubSub');
console.log('user sent message!', msg);
console.groupEnd();
}
sendMessage() {
this.pubsub.publish('event-message', 'Hey, how are you?');
}
}
var room = new ChatRoom();
room.sendMessage();
Mediator
Wherefore:
Mediator is a behavioral design pattern that allows us to expose a unified interface through which the different parts of a system may communicate.
How to:
Using additional object
Mediator
На основе pub-sub строится работа паттерна Mediator, который позволяет наладить коммуникацию между различными компонентами системы. Mediator представляет собой глобальный объект в системе, о котором знают все компоненты системы, при этом компонент может выступать как слушателем события, так и издателем другого события, таким образом налаживая коммуникацию между объектами системы.
Mediator
/*
* Mediator Pattern
*/
class Mediator extends PubSub {
constructor(opts) {
super(); // get handlers
}
attachToObject(obj) {
obj.handlers = [];
obj.publish = this.publish;
obj.subscribe = this.subscribe;
}
}
var mediator = new Mediator();
var myRoom = {
name: 'myRoom'
};
mediator.attachToObject(myRoom);
myRoom.subscribe('connection', function() {
console.group('Mediator');
console.log(`user connected to ${myRoom.name}!`);
console.groupEnd();
}, myRoom);
myRoom.publish('connection');
Facade
Wherefore:
This pattern provides a convenient higher-level interface to a larger body of code, hiding its true underlying complexity.
How to:
Creating set of facade methods and joining them in one place
Facade
Как правило, фасад используется для создания некоторой абстракции, скрывающей за собой совершенно иную реальность. Паттерн «фасад» обеспечивает удобный высокоуровневый интерфейс для больших блоков кода, скрывая за собой их истинную сложность. Относитесь к фасаду, как к упрощенному API, который вы отдаете в пользование другим разработчикам.
Facade
var module = (function() {
var _private = {
i: 5,
get: function() {
console.log('Текущее значение:' + this.i);
},
set: function(val) {
this.i = val;
},
run: function() {
console.log('процесс запущен');
},
jump: function() {
console.log('резкое изменение');
}
};
return {
facade: function(args) {
_private.set(args.val);
_private.get();
if (args.run) {
_private.run();
}
}
}
}());
module.facade({run:true, val:10});
Facade
function Facade() {
function getRequest(url, data, callback) {
setTimeout(function () {
return callback([{ name: 'Arni' }]);
}, 1000);
}
this.getResult = function(url, data, callback) {
getRequest(url, data, callback);
};
return this;
}
Facade
var BannerModule = function(options) {
var params = options || {},
doc = document,
facade = new BannerApiFacade(),
ui = {
'close': doc.getElementById(params.close),
},
helper = {
applyData: function(options) {
}
},
events = {
...
};
//events mapping
for (var key in ui) {
ui[key].onclick = (function(key){
return function(event) {
events[key]();
};
})(key);
}
function successCallback(data) {
};
return {
loadData: function() {
facade
.getResult("GET", 'localhost:4000', successCallback);
}
};
};
var banner = new BannerModule({
close: 'close',
image: 'image',
download: 'download'
});
banner.loadData();
Links
- http://largescalejs.ru/module-pattern/
- https://loftblog.ru/material/razbiraem-js-patterny-na-primere-modulya/
- https://learn.javascript.ru/closures-module
- https://habrahabr.ru/post/270339/
- https://blog.makushev.com/2015/08/03/observer-pub-sub-oop-pattern/
- http://largescalejs.ru/the-facade-pattern/
- http://joshbedo.github.io/JS-Design-Patterns/
- http://www.dofactory.com/javascript/facade-design-pattern
Examples
JS Design Patterns
By Oleg Rovenskyi
JS Design Patterns
Design Patterns
- 887