├── .gitignore ├── patterns ├── behavioral │ ├── visitor │ │ └── readme.md │ ├── template-method │ │ └── readme.md │ ├── interpreter │ │ └── readme.md │ ├── observer │ │ ├── readme.md │ │ ├── spec │ │ │ ├── runner.html │ │ │ └── observer.spec.js │ │ └── observer.js │ ├── iterator │ │ ├── readme.md │ │ ├── spec │ │ │ ├── runner.html │ │ │ └── iterator.spec.js │ │ ├── iterator.ts │ │ └── iterator.js │ ├── memento │ │ ├── readme.md │ │ ├── spec │ │ │ ├── runner.html │ │ │ └── memento.spec.js │ │ └── memento.js │ ├── state │ │ ├── readme.md │ │ ├── spec │ │ │ ├── runner.html │ │ │ └── state.spec.js │ │ └── state.js │ ├── strategy │ │ ├── readme.md │ │ ├── spec │ │ │ ├── runner.html │ │ │ └── strategy.spec.js │ │ └── strategy.js │ ├── command │ │ └── readme.md │ ├── chain-of-responsibility │ │ ├── readme.md │ │ ├── spec │ │ │ ├── runner.html │ │ │ └── chain.spec.js │ │ └── chain.js │ └── mediator │ │ ├── spec │ │ ├── runner.html │ │ └── mediator.spec.js │ │ ├── readme.md │ │ └── mediator.js ├── creational │ ├── factory-method │ │ ├── factory-method.js │ │ ├── spec │ │ │ └── factory-method.spec.js │ │ └── readme.md │ ├── abstract-factory │ │ ├── abstract-factory.js │ │ ├── spec │ │ │ └── abstract-factory.spec.js │ │ └── readme.md │ ├── singleton │ │ ├── readme.md │ │ ├── singleton-static-constructor.js │ │ ├── singleton-closure-static.js │ │ ├── singleton-closure-constructor.js │ │ └── spec │ │ │ ├── runner.html │ │ │ └── singleton.spec.js │ ├── prototype │ │ ├── readme.md │ │ ├── spec │ │ │ ├── runner.html │ │ │ └── prototype.spec.js │ │ └── prototype.js │ ├── builder │ │ └── readme.md │ └── namespace │ │ ├── spec │ │ ├── runner.html │ │ └── namespace.spec.js │ │ ├── readme.md │ │ └── namespace.js ├── enterprise │ ├── registry │ │ ├── readme.md │ │ ├── spec │ │ │ ├── runner.html │ │ │ └── registry.spec.js │ │ └── registry.js │ └── reqres │ │ ├── readme.md │ │ ├── spec │ │ ├── runner.html │ │ └── reqres.spec.js │ │ └── reqres.js └── structural │ ├── flyweight │ └── readme.md │ ├── bridge │ └── readme.md │ ├── module │ ├── readme.md │ ├── spec │ │ ├── runner.html │ │ └── module.spec.js │ └── module.js │ ├── proxy │ ├── readme.md │ ├── spec │ │ ├── runner.html │ │ └── proxy.spec.js │ └── proxy.js │ ├── facade │ ├── readme.md │ ├── spec │ │ ├── runner.html │ │ └── facade.spec.js │ └── facade.js │ ├── composite │ ├── readme.md │ ├── spec │ │ ├── runner.html │ │ └── composite.spec.js │ └── composite.js │ ├── decorator │ ├── readme.md │ ├── spec │ │ ├── runner.html │ │ └── decorator.spec.js │ └── decorator.js │ ├── adapter │ ├── readme.md │ ├── spec │ │ ├── runner.html │ │ └── adapter.spec.js │ └── adapter.js │ └── mixin │ ├── readme.md │ ├── mixin-function.js │ ├── mixin-literal.js │ └── spec │ ├── runner.html │ └── mixin.spec.js ├── tests ├── runner.js └── prepare.js ├── README.md └── assets ├── mocha.css └── underscore.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /node_modules -------------------------------------------------------------------------------- /patterns/behavioral/visitor/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /patterns/behavioral/template-method/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /patterns/creational/factory-method/factory-method.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /patterns/creational/abstract-factory/abstract-factory.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/runner.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | mocha.run(); 3 | })(); -------------------------------------------------------------------------------- /patterns/enterprise/registry/readme.md: -------------------------------------------------------------------------------- 1 | # Реестр / Registry 2 | 3 | -------------------------------------------------------------------------------- /patterns/behavioral/interpreter/readme.md: -------------------------------------------------------------------------------- 1 | # Интерпретатор / Interpreter 2 | 3 | -------------------------------------------------------------------------------- /patterns/enterprise/reqres/readme.md: -------------------------------------------------------------------------------- 1 | # Запрос/Ответ / Request/Response 2 | 3 | -------------------------------------------------------------------------------- /patterns/behavioral/observer/readme.md: -------------------------------------------------------------------------------- 1 | # Наблюдатель / Observer / Publisher-Subscriber 2 | 3 | -------------------------------------------------------------------------------- /patterns/creational/factory-method/spec/factory-method.spec.js: -------------------------------------------------------------------------------- 1 | describe('Фабричный метод / Factory method', function () { 2 | 3 | }); -------------------------------------------------------------------------------- /patterns/creational/singleton/readme.md: -------------------------------------------------------------------------------- 1 | # Синглтон / Одиночка / Singleton 2 | 3 | ### Диаграмма 4 | ... 5 | 6 | ### Описание 7 | ... -------------------------------------------------------------------------------- /patterns/creational/abstract-factory/spec/abstract-factory.spec.js: -------------------------------------------------------------------------------- 1 | describe('Абстрактная фабрика / Abstract factory', function () { 2 | 3 | }); -------------------------------------------------------------------------------- /patterns/structural/flyweight/readme.md: -------------------------------------------------------------------------------- 1 | # Приспособленец / Flyweight 2 | 3 | Использует разделение для эффективной поддержки большого числа мелких объектов. -------------------------------------------------------------------------------- /patterns/structural/bridge/readme.md: -------------------------------------------------------------------------------- 1 | # Мост / Bridge 2 | 3 | Отделяет абстракцию от реализации, благодаря чему появляется возможность независимо изменять то и другое. -------------------------------------------------------------------------------- /tests/prepare.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | assert = chai.assert; 3 | should = chai.should(); 4 | expect = chai.expect; 5 | 6 | mocha.setup('bdd'); 7 | })(); -------------------------------------------------------------------------------- /patterns/creational/prototype/readme.md: -------------------------------------------------------------------------------- 1 | # Прототип / Prototype 2 | 3 | Описывает виды создаваемых объектов с помощью прототипа и создает новые объекты путем его копирования. -------------------------------------------------------------------------------- /patterns/structural/module/readme.md: -------------------------------------------------------------------------------- 1 | # Модуль / Module 2 | 3 | #### Ссылки 4 | 5 | * [Learning JavaScript Design Patterns] (http://addyosmani.com/resources/essentialjsdesignpatterns) -------------------------------------------------------------------------------- /patterns/structural/proxy/readme.md: -------------------------------------------------------------------------------- 1 | # Заместитель / Proxy или Суррогат / Surrogate 2 | 3 | Подменяет другой объект, контролирует доступ к объекту, не изменяя при этом поведения самого объекта. 4 | -------------------------------------------------------------------------------- /patterns/behavioral/iterator/readme.md: -------------------------------------------------------------------------------- 1 | # Итератор / Iterator или Курсор / Cursor 2 | 3 | Дает возможность последовательно обойти все элементы составного объекта, не раскрывая его внутреннего представления. -------------------------------------------------------------------------------- /patterns/structural/facade/readme.md: -------------------------------------------------------------------------------- 1 | # Фасад / Facade 2 | 3 | Предоставляет унифицированный интерфейс к множеству интерфейсов в некоторой подсистеме. Определяет интерфейс более высокого уровня, облегчающий работу с подсистемой. -------------------------------------------------------------------------------- /patterns/creational/builder/readme.md: -------------------------------------------------------------------------------- 1 | # Строитель / Builder 2 | 3 | Отделяет конструирование сложного объекта от его представления, позволяя использовать один и тот же процесс конструирования для создания различных представлений. -------------------------------------------------------------------------------- /patterns/behavioral/memento/readme.md: -------------------------------------------------------------------------------- 1 | # Хранитель / Memento 2 | 3 | Позволяет, не нарушая инкапсуляции, получить и сохранить во внешней памяти внутреннее состояние объекта, чтобы позже объект можно было восстановить точно в таком же состоянии. -------------------------------------------------------------------------------- /patterns/structural/composite/readme.md: -------------------------------------------------------------------------------- 1 | # Компоновщик / Composite 2 | 3 | Группирует объекты в древовидные структуры для представления иерархии типа "часть-целое". Позволяет клиентам работать с единичными объектами так же, как с группами объектов. -------------------------------------------------------------------------------- /patterns/structural/decorator/readme.md: -------------------------------------------------------------------------------- 1 | # Декоратор / Decorator или Оболочка / Wrapper 2 | 3 | Динамически возлагает на объект новые функции. Применяется для расширения имеющейся функциональности и является гибкой альтернативой порождению подклассов. -------------------------------------------------------------------------------- /patterns/behavioral/state/readme.md: -------------------------------------------------------------------------------- 1 | # Состояние / State 2 | 3 | Позволяет объекту варьировать свое поведение при изменении внутреннего состояния. При этом создается впечатление, что поменялся класс объекта. 4 | 5 | TODO: ? Разница между Состоянием и Стратегией -------------------------------------------------------------------------------- /patterns/structural/adapter/readme.md: -------------------------------------------------------------------------------- 1 | # Адаптер / Adapter 2 | 3 | Преобразует интерфейс класса в некоторый другой интерфейс, ожидаемый клиентами. Обеспечивает совместную работу классов, которая была бы невозможна без данного паттерна из-за несовместимости интерфейсов. -------------------------------------------------------------------------------- /patterns/behavioral/strategy/readme.md: -------------------------------------------------------------------------------- 1 | # Стратегия / Strategy 2 | 3 | Паттерн определяет семейство алгоритмов, инкапсулируя их все и позволяя подставлять один вместо другого. Можно менять алгоритм независимо от клиента, который им пользуется. 4 | 5 | TODO: ? Разница между Состоянием и Стратегией -------------------------------------------------------------------------------- /patterns/behavioral/command/readme.md: -------------------------------------------------------------------------------- 1 | # Команда / Command, Действие / Action или Транзакция / Transaction 2 | 3 | Инкапсулирует запрос в виде объекта, позволяя тем самым параметризовывать клиентов типом запроса, устанавливать очередность запросов, протоколировать их и поддерживать отмену выполнения операций. -------------------------------------------------------------------------------- /patterns/creational/factory-method/readme.md: -------------------------------------------------------------------------------- 1 | # Фабричный метод / Factory method 2 | 3 | Определяет интерфейс для создания объектов, при этом выбранный класс инстанцируется подклассами. 4 | 5 | #### Ссылки 6 | 7 | * [Dofactory: Factory method] (http://www.dofactory.com/javascript-factory-method-pattern.aspx) -------------------------------------------------------------------------------- /patterns/structural/mixin/readme.md: -------------------------------------------------------------------------------- 1 | # Примесь / Mixin 2 | 3 | Примесь это класс, реализующий какое-либо чётко выделенное поведение. Используется для уточнения поведения других классов, не предназначен для порождения самостоятельно используемых объектов. 4 | 5 | [Хорошая статья про примеси на Хабре] (http://habrahabr.ru/post/147901/) -------------------------------------------------------------------------------- /patterns/creational/abstract-factory/readme.md: -------------------------------------------------------------------------------- 1 | # Абстрактная фабрика / Abstract factory 2 | 3 | Предоставляет интерфейс для создания семейств, связанных между собой, или независимых объектов, конкретные классы которых неизвестны. 4 | 5 | #### Ссылки 6 | 7 | * [Dofactory: Abstract factory] (http://www.dofactory.com/javascript-abstract-factory-pattern.aspx) -------------------------------------------------------------------------------- /patterns/behavioral/chain-of-responsibility/readme.md: -------------------------------------------------------------------------------- 1 | # Цепочка обязаностей / Chain of Responsibility 2 | 3 | Можно избежать жесткой зависимости отправителя запроса от его получателя, при этом запросом начинает обрабатываться один из нескольких объектов. Объекты-получатели связываются в цепочку и запрос передается по цепочке, пока какой-то объект его не обработает. -------------------------------------------------------------------------------- /patterns/creational/singleton/singleton-static-constructor.js: -------------------------------------------------------------------------------- 1 | function StaticConstructorSingleton() { 2 | 3 | if (typeof StaticConstructorSingleton.instance === 'object') { 4 | return StaticConstructorSingleton.instance; 5 | } 6 | 7 | StaticConstructorSingleton.instance = this; 8 | 9 | return this; 10 | } 11 | 12 | // Публичные методы 13 | StaticConstructorSingleton.prototype.toString = function () { 14 | return '[object StaticConstructorSingleton]'; 15 | } -------------------------------------------------------------------------------- /patterns/creational/singleton/singleton-closure-static.js: -------------------------------------------------------------------------------- 1 | var ClosureStaticSingleton = (function () { 2 | var instance; 3 | 4 | function createInstance() { 5 | // Приватные методы 6 | function _privateMethod () { 7 | 8 | } 9 | 10 | return { 11 | // Публичные методы 12 | }; 13 | } 14 | 15 | return { 16 | getInstance: function () { 17 | if (!instance) { 18 | instance = createInstance(); 19 | } 20 | 21 | return instance; 22 | } 23 | }; 24 | })(); -------------------------------------------------------------------------------- /patterns/creational/singleton/singleton-closure-constructor.js: -------------------------------------------------------------------------------- 1 | var ClosureConstructorSingleton = new function () { 2 | var instance; 3 | 4 | // Конструктор 5 | function ClosureConstructorSingleton() { 6 | if (!instance) { 7 | instance = this; 8 | } else { 9 | return instance; 10 | } 11 | } 12 | 13 | // Приватные методы и свойства 14 | function _privateMethod() { 15 | // 16 | } 17 | 18 | // Публичные методы 19 | ClosureConstructorSingleton.prototype.toString = function () { 20 | return '[object ClosureConstructorSingleton]'; 21 | }; 22 | 23 | return ClosureConstructorSingleton; 24 | }; -------------------------------------------------------------------------------- /patterns/structural/mixin/mixin-function.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Базовый класс 3 | * */ 4 | var BookFunctional = (function () { 5 | 6 | function BookFunctional(author, title) { 7 | this._title = title; 8 | this._author = author; 9 | } 10 | 11 | BookFunctional.prototype.getInfo = function () { 12 | return this._author + ' - ' + this._title; 13 | }; 14 | 15 | return BookFunctional; 16 | })(); 17 | 18 | /** 19 | * Сама примесь 20 | * */ 21 | var BookFunctionalMixin = function () { 22 | 23 | this.getAuthor = function () { 24 | return this._author; 25 | }; 26 | 27 | this.getTitle = function () { 28 | return this._title; 29 | }; 30 | 31 | return this; 32 | }; 33 | 34 | /** 35 | * Применение: BookFunctionalMixin.call(BookFunctional.prototype); 36 | * */ -------------------------------------------------------------------------------- /patterns/structural/mixin/mixin-literal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Базовый класс 3 | * */ 4 | var BookLiteral = (function () { 5 | 6 | function BookLiteral(author, title) { 7 | this._title = title; 8 | this._author = author; 9 | } 10 | 11 | BookLiteral.prototype.getInfo = function () { 12 | return this._author + ' - ' + this._title; 13 | }; 14 | 15 | return BookLiteral; 16 | })(); 17 | 18 | /** 19 | * Сама примесь 20 | * */ 21 | var BookLiteralMixin = { 22 | 23 | getAuthor: function () { 24 | return this._author; 25 | }, 26 | 27 | getTitle: function () { 28 | return this._title; 29 | } 30 | }; 31 | 32 | /** 33 | * Применение: _.extend(BookLiteral.prototype, BookLiteralMixin); 34 | * Метод _.extend() скопирует все методы примеси в объект Book 35 | * */ -------------------------------------------------------------------------------- /patterns/behavioral/state/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - State - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/structural/proxy/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Proxy - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/structural/facade/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Facade - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/structural/module/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Module - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/structural/adapter/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Adapter - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/behavioral/iterator/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Iterator - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/behavioral/memento/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Memento - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /patterns/behavioral/observer/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Observer - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/behavioral/strategy/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Strategy - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/enterprise/registry/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Registry - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/enterprise/reqres/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Request/Response - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/behavioral/mediator/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Mediator - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /patterns/creational/namespace/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Namespace - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/creational/prototype/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Prototype - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/structural/composite/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Composite - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/structural/decorator/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Decorator - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/behavioral/chain-of-responsibility/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Chain of Responsibility - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /patterns/structural/mixin/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Mixin - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /patterns/creational/singleton/spec/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Javascript Design Patterns - Singleton - Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /patterns/creational/prototype/prototype.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Базовый класс 3 | * */ 4 | var Person = (function () { 5 | 6 | function Person(firstname, lastname) { 7 | this.firstname = firstname; 8 | this.lastname = lastname; 9 | } 10 | 11 | Person.prototype.introduce = function () { 12 | return 'My name is ' + this.firstname + ' ' + this.lastname; 13 | }; 14 | 15 | return Person; 16 | })(); 17 | 18 | /** 19 | * Прототип 20 | * */ 21 | var Proto = (function () { 22 | 23 | function Proto(person) { 24 | this.person = person; 25 | } 26 | 27 | Proto.prototype.clone = function () { 28 | var f = function () { 29 | }; 30 | f.prototype = this.person; 31 | return new f(); 32 | }; 33 | 34 | return Proto; 35 | })(); 36 | 37 | /** 38 | * Использование: 39 | * var john = new Person('John', 'Smith'), 40 | * johnPrototype = new Proto(john), 41 | * johnClone = johnPrototype.clone(); 42 | * 43 | * console.log(johnClone.introduce()); 44 | * johnClone будет клоном объекта john 45 | * */ -------------------------------------------------------------------------------- /patterns/structural/decorator/decorator.js: -------------------------------------------------------------------------------- 1 | // Базовый конструктор, который будет декорирован 2 | var Beverage = (function () { 3 | function Beverage(cost, desc) { 4 | this._cost = cost; 5 | this._desc = desc; 6 | } 7 | 8 | Beverage.prototype.getCost = function () { 9 | return this._cost; 10 | }; 11 | 12 | Beverage.prototype.getDescription = function () { 13 | return this._desc; 14 | }; 15 | 16 | return Beverage; 17 | })(); 18 | 19 | // Конструктор декоратора 20 | var Vine = (function () { 21 | function Vine(name, color, cost, desc) { 22 | this._beverage = new Beverage(cost, desc); 23 | this._name = name; 24 | this._color = color; 25 | } 26 | 27 | Vine.prototype.getCost = function () { 28 | return this._beverage.getCost(); 29 | }; 30 | 31 | Vine.prototype.getDescription = function () { 32 | return this._beverage.getDescription(); 33 | }; 34 | 35 | Vine.prototype.getColor = function () { 36 | return this._color; 37 | }; 38 | 39 | Vine.prototype.getName = function () { 40 | return this._name; 41 | }; 42 | 43 | return Vine; 44 | })(); -------------------------------------------------------------------------------- /patterns/behavioral/iterator/iterator.ts: -------------------------------------------------------------------------------- 1 | class Iterator { 2 | private _cursor:number; 3 | private _items:Array; 4 | 5 | constructor(items) { 6 | this._cursor = 0; 7 | this._items = items; 8 | } 9 | 10 | current() { 11 | return this._items[this._cursor]; 12 | } 13 | 14 | first() { 15 | this.reset(); 16 | return this.current(); 17 | } 18 | 19 | next() { 20 | if (this.hasNext()) { 21 | this._cursor++; 22 | return this.current(); 23 | } 24 | } 25 | 26 | previous() { 27 | if (this.hasPrevious()) { 28 | this._cursor--; 29 | return this.current(); 30 | } 31 | } 32 | 33 | last() { 34 | this._cursor = this._items.length - 1; 35 | return this.current(); 36 | } 37 | 38 | hasNext() { 39 | return this._cursor < this._items.length; 40 | } 41 | 42 | hasPrevious() { 43 | return this._cursor > 0; 44 | } 45 | 46 | reset() { 47 | this._cursor = 0; 48 | } 49 | 50 | each(fn) { 51 | var item = this.first(); 52 | for (; this.hasNext(); item = this.next()) { 53 | fn(item); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /patterns/behavioral/strategy/spec/strategy.spec.js: -------------------------------------------------------------------------------- 1 | describe('Стратегия / Strategy', function () { 2 | 3 | var calc; 4 | 5 | beforeEach(function () { 6 | calc = new Calculator(); 7 | }); 8 | 9 | describe('Инстанс', function () { 10 | 11 | it('Calculator', function () { 12 | expect(calc).to.be.instanceOf(Calculator); 13 | }); 14 | 15 | it('Методы', function () { 16 | expect(calc.set).to.be.a('function'); 17 | expect(calc.execute).to.be.a('function'); 18 | }); 19 | }); 20 | 21 | describe('Поведение', function () { 22 | 23 | it('Calculator.execute', function () { 24 | expect(calc.execute('add', 4, 5)).to.equal(9); 25 | expect(calc.execute('subtract', 4, 5)).to.equal(-1); 26 | expect(calc.execute('multiply', 4, 5)).to.equal(20); 27 | expect(calc.execute('divide', 40, 5)).to.equal(8); 28 | }); 29 | 30 | it('Calculator.set', function () { 31 | 32 | calc 33 | .set('pow', function(x, y) { 34 | return Math.pow(x, y); 35 | }); 36 | 37 | expect(calc.execute('pow', 15, 2)).to.equal(225); 38 | 39 | }); 40 | 41 | }); 42 | }); -------------------------------------------------------------------------------- /patterns/creational/namespace/readme.md: -------------------------------------------------------------------------------- 1 | # Пространство имен / Namespace 2 | 3 | Пространства имен помогают уменьшить количество глобальных переменных, необходимых нашим программам, и одновременно избежать конфликтов имен и чрезмерного употребления префиксов. 4 | 5 | ### Пример использования 6 | 7 | Создать неймспейс `App.modules.login`: 8 | 9 | ```js 10 | var App = App || {}; 11 | 12 | Namespace.create(App, 'modules.login'); 13 | ``` 14 | 15 | Создать неймспейс с объектом, который хранит коды клавиш `App.keys`: 16 | 17 | ```js 18 | Namespace.create(App, 'keys', { 19 | BACKSPACE: 8, 20 | TAB: 9, 21 | ENTER: 13, 22 | SHIFT: 16, 23 | CTRL: 17, 24 | ALT: 18, 25 | ESC: 27, 26 | SPACE: 32, 27 | LEFT_ARROW: 37, 28 | UP_ARROW: 38, 29 | RIGHT_ARROW: 39, 30 | DOWN_ARROW: 40 31 | }); 32 | 33 | console.log(App.keys.TAB); // => 9 34 | ``` 35 | 36 | Создать неймспейс с функцией-фабрикой 37 | 38 | ```js 39 | Namespace.create(App, 'entities.user', function () { 40 | function User(name) { 41 | this._name = name; 42 | } 43 | 44 | User.prototype.introduce = function () { 45 | console.log('Привет, меня зовут ' + this._name); 46 | }; 47 | 48 | return User; 49 | }); 50 | 51 | var user = new App.entities.user('Козьма Прутков'); 52 | user.introduce(); // => 'Привет, меня зовут Козьма Прутков' 53 | ``` 54 | 55 | #### Ссылки 56 | 57 | + [Пространство имен в YUI3] (http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_namespace) 58 | + [Статья от Владимира Кузнецова] (http://noteskeeper.ru/264/) 59 | -------------------------------------------------------------------------------- /patterns/creational/namespace/namespace.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Объект приложения, внутри которого будут определяться неймспейсы 3 | * */ 4 | var Namespace = (function () { 5 | 6 | /** 7 | * Создает экземпляр 8 | * @constructor 9 | */ 10 | function Namespace() { 11 | } 12 | 13 | /** 14 | * Возвращает блок пространства имен 15 | * @param {object|function} unit Блок 16 | * @returns {*} 17 | */ 18 | function getUnit(unit) { 19 | 20 | var types = { 21 | 'function': function () { 22 | return new unit(); 23 | }, 24 | 'object': function () { 25 | return unit; 26 | } 27 | }, type = typeof unit; 28 | 29 | return types[type] ? types[type]() : undefined; 30 | } 31 | 32 | /** 33 | * Метод, который создает неймспейс 34 | * @param target {object} объект приложения, в котором создается неймспейс 35 | * @param hash {string} строка неймспейса 36 | * @param unit {(object|function)} модуль приложения 37 | * */ 38 | Namespace.prototype.create = function (target, hash, unit) { 39 | var parts = hash.split('.'), 40 | length = parts.length, 41 | i = 0, 42 | current = target; 43 | 44 | for (; i < length; i++) { 45 | current[parts[i]] = current[parts[i]] || ( i === length - 1 && getUnit(unit)) || {}; 46 | current = current[parts[i]]; 47 | } 48 | 49 | return current; 50 | }; 51 | 52 | return new Namespace(); 53 | })(); -------------------------------------------------------------------------------- /patterns/structural/decorator/spec/decorator.spec.js: -------------------------------------------------------------------------------- 1 | describe('Декоратор / Decorator или Оболочка / Wrapper', function() { 2 | 3 | var beverage, vine; 4 | 5 | before(function () { 6 | beverage = new Beverage(100, 'Unknown beverage'); 7 | vine = new Vine('Chateau Rieussec', 'white', 250, 'Sauternes 1-er Grand Cru Classe AOC'); 8 | }); 9 | 10 | describe('Инстансы', function () { 11 | it("Beverage", function () { 12 | expect(beverage).to.be.an.instanceOf(Beverage); 13 | expect(beverage.getCost).to.be.a('function'); 14 | expect(beverage.getDescription).to.be.a('function'); 15 | }); 16 | 17 | it("Vine", function () { 18 | expect(vine).to.be.an.instanceOf(Vine); 19 | expect(vine.getCost).to.be.a('function'); 20 | expect(vine.getDescription).to.be.a('function'); 21 | expect(vine.getName).to.be.a('function'); 22 | expect(vine.getColor).to.be.a('function'); 23 | }); 24 | }); 25 | 26 | describe('Методы', function () { 27 | it("Метод getName()", function () { 28 | expect(vine.getName()).to.equal('Chateau Rieussec'); 29 | }); 30 | 31 | it("Метод getColor()", function () { 32 | expect(vine.getColor()).to.equal('white'); 33 | }); 34 | 35 | it("Метод getCost()", function () { 36 | expect(vine.getCost()).to.equal(250); 37 | }); 38 | 39 | it("Метод getName()", function () { 40 | expect(vine.getDescription()).to.equal('Sauternes 1-er Grand Cru Classe AOC'); 41 | }); 42 | }); 43 | }); -------------------------------------------------------------------------------- /patterns/behavioral/observer/observer.js: -------------------------------------------------------------------------------- 1 | var Observer = (function () { 2 | /** 3 | * Создает экземпляр наблюдателя 4 | * @constructor 5 | */ 6 | function Observer() { 7 | this._handlers = []; 8 | } 9 | 10 | /** 11 | * Добавляет подписчика 12 | * @param {function} handler Функция-подписчик 13 | * @returns {Observer} 14 | */ 15 | Observer.prototype.subscribe = function (handler) { 16 | this._handlers.push(handler); 17 | return this; 18 | }; 19 | 20 | /** 21 | * Удаляет подписчика 22 | * @param {function} handler Функция-подписчик 23 | * @returns {Observer} 24 | */ 25 | Observer.prototype.unsubscribe = function (handler) { 26 | var i = 0, length = this._handlers.length; 27 | 28 | for (; i < length; i++) { 29 | if (this._handlers[i] === handler) { 30 | this._handlers.splice(i, 1); 31 | break; 32 | } 33 | } 34 | 35 | return this; 36 | }; 37 | 38 | /** 39 | * Инициирует запуск всех подписчиков (Публикует) 40 | * @param {*} data Данные, которые будут переданы в подписчики 41 | * @returns {Observer} 42 | */ 43 | Observer.prototype.publish = function (data) { 44 | var i = 0, length = this._handlers.length; 45 | 46 | for (; i < length; i++) { 47 | this._handlers[i](data); 48 | } 49 | 50 | return this; 51 | }; 52 | 53 | /** 54 | * Удаляет подписчиков 55 | * @returns {Observer} 56 | */ 57 | Observer.prototype.reset = function () { 58 | this._handlers = []; 59 | return this; 60 | }; 61 | 62 | return Observer; 63 | })(); -------------------------------------------------------------------------------- /patterns/behavioral/mediator/readme.md: -------------------------------------------------------------------------------- 1 | # Посредник / Mediator 2 | 3 | Определяет объект, в котором инкапсулировано знание о том, как взаимодействуют объекты из некоторого множества. Способствует уменьшению числа связей между объектами, позволяя им работать без явных ссылок друг на друга. Это, в свою очередь дает возможность независимо изменять схему взаимодействия. 4 | 5 | ### Пример использования 6 | 7 | Создадим экземпляр медиатора: 8 | ```js 9 | var mediator = new Mediator(); // : - разделитель неймспейсов 10 | ``` 11 | 12 | Создадим обработчик для канала `lorem`: 13 | ```js 14 | mediator.on('lorem', function (data) { 15 | console.log('My namespace is ' + data.namespace); 16 | }); 17 | ``` 18 | 19 | Создадим обработчик для вложенного канала 'lorem:ipsum': 20 | ```js 21 | mediator.on('lorem:ipsum', function (data) { 22 | console.log('My namespace is ' + data.namespace); 23 | }); 24 | ``` 25 | 26 | Вызовем оба обработчика: 27 | ```js 28 | mediator.trigger('lorem', { namespace: 'lorem' }); 29 | mediator.trigger('lorem:ipsum', { namespace: 'lorem:ipsum' }); 30 | ``` 31 | 32 | В консоли увидим: 33 | ```js 34 | My namespace is lorem 35 | My namespace is lorem:ipsum 36 | ``` 37 | 38 | Вызовем все обработчики из неймспейса `lorem`: 39 | ```js 40 | mediator.broadcast('lorem', { namespace: 'lorem' }); 41 | ``` 42 | 43 | В консоли так же увидим: 44 | ```js 45 | My namespace is lorem 46 | My namespace is lorem:ipsum 47 | ``` 48 | 49 | Удалим обработчики для неймспейса `lorem` с учетом вложенности: 50 | ```js 51 | mediator.off('lorem', true); 52 | // Такой вызов удалит обработчики как самого неймспейса lorem, так и вложенного в него lorem:ipsum 53 | ``` 54 | 55 | #### Ссылки 56 | 57 | * [Пример простого медиатора в виде amd-модуля (gist)] (https://gist.github.com/instanceofpro/9184609) -------------------------------------------------------------------------------- /patterns/structural/facade/facade.js: -------------------------------------------------------------------------------- 1 | // Подсистемы, которые будут сокрыты фасадом 2 | function Cash(money) { 3 | this._money = money; 4 | } 5 | 6 | Cash.prototype.pay = function (payment) { 7 | // True - если денег хватило на покупки, false - если не хватило 8 | return this._money > payment; 9 | }; 10 | 11 | function Cart() { 12 | this._items = []; // Корзина пустая 13 | } 14 | 15 | Cart.prototype.add = function (item) { 16 | this._items.push(item); 17 | return this; 18 | }; 19 | 20 | Cart.prototype.clean = function () { 21 | this._items = []; 22 | return this; 23 | }; 24 | 25 | Cart.prototype.getCost = function () { 26 | var i = 0, sum = 0, length = this._items.length; 27 | 28 | for(; i < length; i++){ 29 | sum += this._items[i]; 30 | } 31 | 32 | return sum; 33 | }; 34 | 35 | function Warehouse(goods) { 36 | this._goods = goods; 37 | 38 | /** Goods sample 39 | * { 40 | * milk: { count: 200, price: 6 }, 41 | * bread: { count: 150, price: 5 }, 42 | * meat: { count: 120, price: 20 } 43 | * } 44 | * */ 45 | } 46 | 47 | Warehouse.prototype.getItem = function (item) { 48 | this._goods[item].count--; 49 | return this._goods[item].price; 50 | }; 51 | 52 | Warehouse.prototype.getItemCount = function (item) { 53 | return this._goods[item].count; 54 | }; 55 | 56 | 57 | 58 | // Фасад 59 | function Supermarket(shoppingList, money, goods) { 60 | this._list = shoppingList; 61 | this._money = money; 62 | 63 | this._cash = new Cash(this._money); 64 | this._cart = new Cart(); 65 | this._store = new Warehouse(goods); 66 | } 67 | 68 | Supermarket.prototype.goShopping = function () { 69 | var i = 0, length = this._list.length; 70 | 71 | for(;i < length; i++) { 72 | this._cart.add(this._store.getItem(this._list[i])); 73 | } 74 | 75 | return this._cash.pay(this._cart.getCost()); 76 | } -------------------------------------------------------------------------------- /patterns/behavioral/chain-of-responsibility/chain.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Цепочка обязаностей / Chain of Responsibility 3 | * */ 4 | var TodoList = (function () { 5 | 6 | /** 7 | * Создает экземпляр TodoList 8 | * @constructor 9 | * @this {TodoList} 10 | */ 11 | function TodoList() { 12 | this._todos = {}; 13 | } 14 | 15 | /** 16 | * Добавляет дело в список 17 | * @param {string} title Заголовок дела 18 | * @param {string} text Текст дела 19 | * @returns {TodoList} Ссылка на самого себя 20 | */ 21 | TodoList.prototype.add = function (title, text) { 22 | if (this._todos[title] === undefined) { 23 | this._todos[title] = text; 24 | } 25 | 26 | return this; 27 | }; 28 | 29 | /** 30 | * Отдает текст дела по его заголовку 31 | * @param {string} title Заголовок дела 32 | * @returns {string} Текст дела 33 | */ 34 | TodoList.prototype.get = function (title) { 35 | return this._todos[title]; 36 | }; 37 | 38 | /** 39 | * Удаляет дело по его заголовку 40 | * @param {string} title Заголовк дела 41 | * @returns {TodoList} 42 | */ 43 | TodoList.prototype.remove = function (title) { 44 | delete this._todos[title]; 45 | 46 | return this; 47 | }; 48 | 49 | /** 50 | * Редактирует заведенное дело 51 | * @param {string} title Заголовок дела 52 | * @param {string} text Текст дела 53 | * @returns {TodoList} 54 | */ 55 | TodoList.prototype.edit = function (title, text) { 56 | if (this._todos[title] !== undefined) { 57 | this._todos[title] = text; 58 | } 59 | 60 | return this; 61 | }; 62 | 63 | /** 64 | * Очищает список дел 65 | * @returns {TodoList} 66 | */ 67 | TodoList.prototype.clean = function () { 68 | this._todos = {}; 69 | 70 | return this; 71 | }; 72 | 73 | return TodoList; 74 | })(); -------------------------------------------------------------------------------- /patterns/creational/prototype/spec/prototype.spec.js: -------------------------------------------------------------------------------- 1 | describe('Прототип / Prototype', function () { 2 | 3 | var john, johnProto; 4 | 5 | before(function () { 6 | john = new Person('John', 'Doe'); 7 | johnProto = new Proto(john); 8 | }); 9 | 10 | describe('Классы', function () { 11 | 12 | it('Person является классом', function () { 13 | expect(Person).to.be.a('function'); 14 | expect(john).to.be.an.instanceOf(Person); 15 | }); 16 | 17 | it('Экземпляр класса Person имеет метод introduce()', function() { 18 | expect(john.introduce).to.be.a('function'); 19 | expect(john.introduce()).to.equal('My name is John Doe'); 20 | }); 21 | 22 | it('Proto является классом', function () { 23 | expect(Proto).to.be.a('function'); 24 | expect(johnProto).to.be.an.instanceOf(Proto); 25 | }); 26 | 27 | it('Экземпляр класса Proto имеет метод clone()', function() { 28 | expect(johnProto.clone).to.be.a('function'); 29 | }); 30 | 31 | }); 32 | 33 | describe('Поведение', function () { 34 | 35 | var johnClone, johnAnotherClone; 36 | 37 | before(function () { 38 | johnClone = johnProto.clone(); 39 | johnAnotherClone = johnProto.clone(); 40 | }); 41 | 42 | it('Метод .clone() возвращает объект', function () { 43 | expect(johnClone).to.be.an('object'); 44 | }); 45 | 46 | it('Возвращаемые методом .clone() объекты должны быть копиями экземпляра класса Person', function () { 47 | expect(john.introduce() === johnClone.introduce()).to.be.true; 48 | }); 49 | 50 | it('Копии не должны быть одним и тем же объектом', function () { 51 | expect(john === johnClone).to.be.false; 52 | expect(john === johnAnotherClone).to.be.false; 53 | expect(johnAnotherClone === johnClone).to.be.false; 54 | }); 55 | }); 56 | 57 | }); -------------------------------------------------------------------------------- /patterns/structural/adapter/adapter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Англичанин 3 | * */ 4 | var Englishman = (function () { 5 | 6 | function Englishman(firstname, lastname) { 7 | this._firstname = firstname; 8 | this._lastname = lastname; 9 | } 10 | 11 | Englishman.prototype.getFirstname = function () { 12 | return this._firstname; 13 | }; 14 | 15 | Englishman.prototype.getLastname = function () { 16 | return this._lastname; 17 | }; 18 | 19 | Englishman.prototype.introduce = function () { 20 | return 'My name is ' + this._firstname + ' ' + this._lastname; 21 | }; 22 | 23 | Englishman.prototype.greet = function () { 24 | return 'Hello! How are you?'; 25 | }; 26 | 27 | return Englishman; 28 | })(); 29 | 30 | /** 31 | * Итальянец 32 | * */ 33 | var Italian = (function () { 34 | 35 | function Italian(firstname, lastname) { 36 | this._firstname = firstname; 37 | this._lastname = lastname; 38 | } 39 | 40 | Italian.prototype.getFirstname = function () { 41 | return this._firstname; 42 | }; 43 | 44 | Italian.prototype.getLastname = function () { 45 | return this._lastname; 46 | }; 47 | 48 | Italian.prototype.introdurre = function () { 49 | return 'Il mio nome è ' + this._firstname + ' ' + this._lastname; 50 | }; 51 | 52 | Italian.prototype.ciao = function () { 53 | return 'Ciao! Come stai?'; 54 | }; 55 | 56 | return Italian; 57 | })(); 58 | 59 | /** 60 | * Адаптер итальянца к англичанину 61 | * */ 62 | var AdaptToEnglish = (function () { 63 | 64 | function Adapter(italian) { 65 | this._firstname = italian.getFirstname(); 66 | this._lastname = italian.getLastname(); 67 | } 68 | 69 | Adapter.prototype.introduce = function () { 70 | return 'My name is ' + this._firstname + ' ' + this._lastname; 71 | }; 72 | 73 | Adapter.prototype.greet = function () { 74 | return 'Hello! How are you?'; 75 | }; 76 | 77 | return Adapter; 78 | })(); -------------------------------------------------------------------------------- /patterns/enterprise/registry/spec/registry.spec.js: -------------------------------------------------------------------------------- 1 | describe('Реестр / Registry', function() { 2 | 3 | describe('Простой реестр', function () { 4 | 5 | var registry; 6 | 7 | before(function () { 8 | registry = new SimpleRegistry(); 9 | }); 10 | 11 | it('Инстанс', function () { 12 | expect(SimpleRegistry).to.be.a('function'); 13 | expect(registry).to.be.an.instanceOf(SimpleRegistry); 14 | expect(registry.set).to.be.a('function'); 15 | expect(registry.get).to.be.a('function'); 16 | expect(registry.has).to.be.a('function'); 17 | }); 18 | 19 | it('Поведение', function (){ 20 | registry.set('foo', 'bar'); 21 | expect(registry.has('foo')).to.be.true; 22 | expect(registry.get('foo')).to.equal('bar'); 23 | 24 | expect(registry.has('hello')).to.be.false; 25 | expect(registry.get('hello')).to.be.undefined; 26 | }); 27 | 28 | }); 29 | 30 | describe('Реестр-одиночка', function () { 31 | 32 | var registry, another; 33 | 34 | before(function () { 35 | registry = new SingletonRegistry(); 36 | another = SingletonRegistry(); 37 | }); 38 | 39 | it('Инстанс', function () { 40 | expect(SingletonRegistry).to.be.a('function'); 41 | expect(registry).to.equal(another); 42 | expect(registry).to.be.an.instanceOf(SingletonRegistry); 43 | expect(registry.set).to.be.a('function'); 44 | expect(registry.get).to.be.a('function'); 45 | expect(registry.has).to.be.a('function'); 46 | }); 47 | 48 | it('Поведение', function (){ 49 | registry.set('foo', 'bar'); 50 | expect(registry.has('foo')).to.be.true; 51 | expect(registry.get('foo')).to.equal('bar'); 52 | expect(another.has('foo')).to.be.true; 53 | expect(another.get('foo')).to.equal('bar'); 54 | 55 | expect(registry.has('hello')).to.be.false; 56 | expect(registry.get('hello')).to.be.undefined; 57 | expect(another.has('hello')).to.be.false; 58 | expect(another.get('hello')).to.be.undefined; 59 | }); 60 | }); 61 | }); -------------------------------------------------------------------------------- /patterns/structural/composite/composite.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Класс-ветка, который может в себе содержать как такие же ветки, так и листья 3 | * */ 4 | var Branch = (function () { 5 | 6 | function Branch(name) { 7 | this._name = name; 8 | this._children = []; 9 | } 10 | 11 | Branch.prototype.addChild = function (child) { 12 | this._children.push(child); 13 | 14 | return this; 15 | }; 16 | 17 | Branch.prototype.removeChild = function (child) { 18 | var length = this._children.length, 19 | i = 0; 20 | 21 | for (; i < length; i++) { 22 | if (this._children[i] === child) { 23 | this._children.splice(i, 1); 24 | return; 25 | } 26 | } 27 | }; 28 | 29 | Branch.prototype.getChild = function (item) { 30 | return this._children[item]; 31 | }; 32 | 33 | Branch.prototype.hasChild = function (child) { 34 | var length = this._children.length, 35 | i = 0; 36 | 37 | for (; i < length; i++) { 38 | if (this._children[i] === child) { 39 | return true; 40 | } 41 | } 42 | 43 | return false; 44 | }; 45 | 46 | Branch.prototype.hasChildren = function() { 47 | return this.getChildrenCount() > 0; 48 | }; 49 | 50 | Branch.prototype.getName = function() { 51 | return this._name; 52 | }; 53 | 54 | Branch.prototype.getChildrenCount = function () { 55 | return this._children.length; 56 | }; 57 | 58 | Branch.prototype.print = function() { 59 | var names = [], 60 | i = 0, 61 | length = this._children.length; 62 | 63 | if (length) { 64 | for (; i < length; i++) { 65 | names.push(this._children[i].getName()); 66 | } 67 | } 68 | 69 | return this._name + ' -> [' + names.join(', ') + ']'; 70 | }; 71 | 72 | return Branch; 73 | })(); 74 | 75 | 76 | /** 77 | * Класс-лист, который может принадлежать ветке 78 | * */ 79 | var Leaf = (function () { 80 | 81 | function Leaf(name) { 82 | this._name = name; 83 | } 84 | 85 | Leaf.prototype.getName = function() { 86 | return this._name; 87 | }; 88 | 89 | return Leaf; 90 | })(); -------------------------------------------------------------------------------- /patterns/creational/namespace/spec/namespace.spec.js: -------------------------------------------------------------------------------- 1 | describe('Пространство имен / Namespace', function () { 2 | 3 | describe('Инстанс', function () { 4 | 5 | it('Namespace', function () { 6 | expect(Namespace).to.be.a('object'); 7 | }); 8 | 9 | it('Методы', function () { 10 | expect(Namespace.create).to.exist; 11 | expect(Namespace.create).to.be.a('function'); 12 | }); 13 | }); 14 | 15 | describe('Поведение', function () { 16 | 17 | it('Создание неймспейсов', function () { 18 | var App = App || {}; 19 | 20 | expect(Namespace.create(App, 'modules.login')).to.equal(App.modules.login); 21 | expect(Namespace.create(App.modules, 'logout')).to.equal(App.modules.logout); 22 | expect(Namespace.create(App, 'modules')).to.equal(App.modules); 23 | expect(Namespace.create(App.modules.logout, 'success', {})).to.equal(App.modules.logout.success); 24 | expect(Namespace.create(App, 'modules.logout.fail', {})).to.equal(App.modules.logout.fail); 25 | }); 26 | 27 | it('Создание неймспейсов с объектом', function () { 28 | var AnotherApp = AnotherApp || {}; 29 | 30 | expect(Namespace.create(AnotherApp, 'modules.login', { foo: 'bar' })).to.equal(AnotherApp.modules.login); 31 | expect(AnotherApp.modules.login).to.have.property('foo'); 32 | }); 33 | 34 | it('Создание неймспейсов с функциями-фабриками', function () { 35 | var FnApp = FnApp || {}; 36 | 37 | expect(Namespace.create(FnApp, 'entities.user', function () { 38 | function User(name) { 39 | this._name = name; 40 | } 41 | 42 | User.prototype.introduce = function () { 43 | return 'Привет, меня зовут ' + this._name; 44 | }; 45 | 46 | return User; 47 | })).to.equal(FnApp.entities.user); 48 | 49 | var user = new FnApp.entities.user('Козьма Прутков'); 50 | 51 | expect(user).to.be.instanceOf(FnApp.entities.user); 52 | expect(user.introduce).to.be.a('function'); 53 | expect(user.introduce()).to.equal('Привет, меня зовут Козьма Прутков'); 54 | }); 55 | 56 | }); 57 | }); -------------------------------------------------------------------------------- /patterns/behavioral/state/spec/state.spec.js: -------------------------------------------------------------------------------- 1 | describe('Состояние / State', function () { 2 | 3 | var calc, add, subtract, multiply, divide; 4 | 5 | before(function () { 6 | add = new Add(); 7 | subtract = new Subtract(); 8 | multiply = new Multiply(); 9 | divide = new Divide(); 10 | calc = new Calculator(add); 11 | }); 12 | 13 | describe('Инстанс', function () { 14 | 15 | it('Calculator и методы', function () { 16 | expect(calc).to.be.instanceOf(Calculator); 17 | expect(calc.set).to.be.a('function'); 18 | expect(calc.execute).to.be.a('function'); 19 | }); 20 | 21 | it('Add и методы', function () { 22 | expect(add).to.be.instanceOf(Add); 23 | expect(add.execute).to.be.a('function'); 24 | }); 25 | 26 | it('Subtract и методы', function () { 27 | expect(subtract).to.be.instanceOf(Subtract); 28 | expect(subtract.execute).to.be.a('function'); 29 | }); 30 | 31 | }); 32 | 33 | describe('Поведение', function () { 34 | 35 | it('Сложение', function () { 36 | expect(calc.execute(4, 5)).to.equal(9); 37 | expect(calc.execute(0, 0)).to.equal(0); 38 | expect(calc.execute(-7, 3)).to.equal(-4); 39 | expect(calc.execute(-2, -8)).to.equal(-10); 40 | }); 41 | 42 | it('Вычитание', function () { 43 | calc.set(subtract); 44 | 45 | expect(calc.execute(4, 5)).to.equal(-1); 46 | expect(calc.execute(0, 0)).to.equal(0); 47 | expect(calc.execute(-7, 3)).to.equal(-10); 48 | expect(calc.execute(-2, -8)).to.equal(6); 49 | }); 50 | 51 | it('Умножение', function () { 52 | calc.set(multiply); 53 | 54 | expect(calc.execute(4, 5)).to.equal(20); 55 | expect(calc.execute(0, 0)).to.equal(0); 56 | expect(calc.execute(-7, 3)).to.equal(-21); 57 | expect(calc.execute(-2, -8)).to.equal(16); 58 | }); 59 | 60 | it('Деление', function () { 61 | calc.set(divide); 62 | 63 | expect(calc.execute(40, 5)).to.equal(8); 64 | expect(calc.execute(2, 4)).to.equal(0.5); 65 | expect(calc.execute(-6, 3)).to.equal(-2); 66 | expect(calc.execute(-2, -8)).to.equal(0.25); 67 | }); 68 | }); 69 | }); -------------------------------------------------------------------------------- /patterns/behavioral/chain-of-responsibility/spec/chain.spec.js: -------------------------------------------------------------------------------- 1 | describe('Цепочка обязаностей / Chain of Responsibility', function () { 2 | 3 | var todos; 4 | 5 | describe('Инстанс', function () { 6 | 7 | beforeEach(function () { 8 | todos = new TodoList(); 9 | }); 10 | 11 | it('TodoList', function () { 12 | expect(todos).to.be.instanceOf(TodoList); 13 | }); 14 | 15 | it('Методы', function () { 16 | expect(todos.add).to.be.a('function'); 17 | expect(todos.edit).to.be.a('function'); 18 | expect(todos.remove).to.be.a('function'); 19 | expect(todos.clean).to.be.a('function'); 20 | expect(todos.get).to.be.a('function'); 21 | }); 22 | }); 23 | 24 | describe('Поведение', function () { 25 | 26 | beforeEach(function () { 27 | todos = new TodoList(); 28 | }); 29 | 30 | it('TodoList.add', function () { 31 | expect(todos.add('Первая запись', 'Текст первой записи')).to.equal(todos); 32 | }); 33 | 34 | it('TodoList.get', function () { 35 | todos.add('Запись', 'Текст записи'); 36 | 37 | expect(todos.get('Запись')).to.equal('Текст записи'); 38 | expect(todos.get('Несуществующая запись')).to.be.undefined; 39 | }); 40 | 41 | it('TodoList.edit', function () { 42 | todos.add('Запись', 'Текст записи'); 43 | 44 | expect(todos.edit('Запись', 'Отредактированный текст записи')).to.equal(todos); 45 | expect(todos.get('Запись')).to.equal('Отредактированный текст записи'); 46 | }); 47 | 48 | it('TodoList.remove', function () { 49 | todos.add('Запись', 'Текст записи'); 50 | 51 | expect(todos.remove('Запись')).to.equal(todos); 52 | expect(todos.get('Запись')).to.be.undefined; 53 | }); 54 | 55 | 56 | it('TodoList.clean', function () { 57 | todos 58 | .add('Первая запись', 'Текст первой записи') 59 | .add('Вторая запись', 'Текст второй записи') 60 | .add('Третья запись', 'Текст третьей записи') 61 | .add('Четвертая запись', 'Текст четвертой записи'); 62 | 63 | expect(todos.clean()).to.equal(todos); 64 | expect(todos.get('Первая запись')).to.be.undefined; 65 | expect(todos.get('Вторая запись')).to.be.undefined; 66 | expect(todos.get('Третья запись')).to.be.undefined; 67 | expect(todos.get('Четвертая запись')).to.be.undefined; 68 | }); 69 | }); 70 | 71 | }); -------------------------------------------------------------------------------- /patterns/structural/composite/spec/composite.spec.js: -------------------------------------------------------------------------------- 1 | describe('Компоновщик / Composite', function () { 2 | 3 | describe('Инстанс', function () { 4 | 5 | var branch, leaf; 6 | 7 | before(function () { 8 | branch = new Branch('Меню'); 9 | leaf = new Leaf('Пункт меню'); 10 | }); 11 | 12 | it('Branch', function () { 13 | expect(Branch).to.be.a('function'); 14 | expect(branch).to.be.an.instanceOf(Branch); 15 | 16 | expect(branch.getName).to.be.a('function'); 17 | expect(branch.addChild).to.be.a('function'); 18 | expect(branch.removeChild).to.be.a('function'); 19 | expect(branch.getChild).to.be.a('function'); 20 | expect(branch.hasChild).to.be.a('function'); 21 | expect(branch.hasChildren).to.be.a('function'); 22 | expect(branch.getChildrenCount).to.be.a('function'); 23 | expect(branch.print).to.be.a('function'); 24 | 25 | }); 26 | 27 | it('Leaf', function() { 28 | expect(Leaf).to.be.a('function'); 29 | expect(leaf).to.be.an.instanceOf(Leaf); 30 | 31 | expect(leaf.getName).to.be.a('function'); 32 | }); 33 | 34 | }); 35 | 36 | describe('Поведение', function () { 37 | 38 | var menu, subMenu, leaf; 39 | 40 | beforeEach(function () { 41 | menu = new Branch('Меню ресторана'); 42 | subMenu = new Branch('Винная карта'); 43 | leaf = new Leaf('Вермут'); 44 | }); 45 | 46 | it('addChild', function() { 47 | expect(menu.getChildrenCount()).to.equal(0); 48 | menu.addChild(new Branch('Алкоголь')); 49 | expect(menu.getChildrenCount()).to.equal(1); 50 | }); 51 | 52 | it('removeChild', function() { 53 | expect(menu.hasChildren()).to.be.false; 54 | menu.addChild(subMenu); 55 | expect(menu.hasChildren()).to.be.true; 56 | menu.removeChild(subMenu); 57 | expect(menu.hasChildren()).to.be.false; 58 | }); 59 | 60 | it('getChild', function() { 61 | menu.addChild(subMenu); 62 | expect(menu.getChild(0)).to.equal(subMenu); 63 | expect(menu.getChild(0).getName()).to.equal('Винная карта'); 64 | }); 65 | 66 | it('hasChild', function() { 67 | menu.addChild(subMenu); 68 | expect(menu.hasChild(subMenu)).to.true; 69 | expect(menu.hasChild(leaf)).to.false; 70 | 71 | subMenu.addChild(leaf); 72 | expect(subMenu.hasChild(leaf)).to.true; 73 | }); 74 | }); 75 | 76 | }); -------------------------------------------------------------------------------- /patterns/behavioral/strategy/strategy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Калькулятор - стратегия 3 | * */ 4 | var Calculator = (function () { 5 | 6 | /** 7 | * Создает экземпляр 8 | * @constructor 9 | */ 10 | function Calculator() { 11 | 12 | /** 13 | * Встроенные алгоритмы 14 | * @private 15 | */ 16 | this._methods = { 17 | /** 18 | * Складывает два операнда 19 | * @param {number} x Первый операнд 20 | * @param {number} y Второй операнд 21 | * @returns {number} Сумма 22 | */ 23 | add: function (x, y) { 24 | return x + y; 25 | }, 26 | 27 | /** 28 | * Вычитает из одного операнда другой 29 | * @param {number} x Уменьшаемое 30 | * @param {number} y Вычитаемое 31 | * @returns {number} Разница 32 | */ 33 | subtract: function (x, y) { 34 | return x - y; 35 | }, 36 | 37 | /** 38 | * Перемножает два операнда 39 | * @param {number} x Первый операнд 40 | * @param {number} y Второй операнд 41 | * @returns {number} Произведение 42 | */ 43 | multiply: function (x, y) { 44 | return x * y; 45 | }, 46 | 47 | /** 48 | * Делит один операнд на другой 49 | * @param {number} x Делимое 50 | * @param {number} y Делитель 51 | * @returns {number} Результат 52 | */ 53 | divide: function (x, y) { 54 | return x / y; 55 | } 56 | }; 57 | } 58 | 59 | /** 60 | * Добавляет новый метод в калькулятор 61 | * @param {string} name Имя метода 62 | * @param {function} method Функция-метод 63 | * @returns {Calculator} 64 | */ 65 | Calculator.prototype.set = function (name, method) { 66 | if (this._methods[name] === undefined) { 67 | this._methods[name] = method; 68 | } 69 | 70 | return this; 71 | }; 72 | 73 | /** 74 | * Вызывает метод и передает в него параметры 75 | * @param {string} method Имя метода 76 | * @param {number} x Первый операнд 77 | * @param {number} y Второй операнд 78 | * @returns {number} Результат 79 | */ 80 | Calculator.prototype.execute = function (method, x, y) { 81 | if (this._methods[method] !== undefined) { 82 | return this._methods[method](x, y); 83 | } else { 84 | throw new Error('Method does not exists!'); 85 | } 86 | }; 87 | 88 | return Calculator; 89 | })(); -------------------------------------------------------------------------------- /patterns/behavioral/iterator/iterator.js: -------------------------------------------------------------------------------- 1 | var Iterator = (function () { 2 | /** 3 | * Создает экземпляр итератора 4 | * @param {array} items Итерируемый массив 5 | * @constructor 6 | */ 7 | function Iterator(items) { 8 | this._cursor = 0; 9 | this._items = items; 10 | } 11 | 12 | /** 13 | * Отдает текущий элемент массива 14 | * @returns {*} 15 | */ 16 | Iterator.prototype.current = function () { 17 | return this._items[this._cursor]; 18 | }; 19 | 20 | /** 21 | * Сдвигает курсор в начальное положение и отдает первый элемент массив 22 | * @returns {*} 23 | */ 24 | Iterator.prototype.first = function () { 25 | this.reset(); 26 | return this.current(); 27 | }; 28 | 29 | /** 30 | * Сдвигает курсор на один шаг вперед и отдает элемент массива 31 | * @returns {*} 32 | */ 33 | Iterator.prototype.next = function () { 34 | if (this.hasNext()) { 35 | this._cursor++; 36 | return this.current(); 37 | } 38 | }; 39 | 40 | /** 41 | * Сдвигает курсор на одни шаг назад и отдает элемент массива 42 | * @returns {*} 43 | */ 44 | Iterator.prototype.previous = function () { 45 | if (this.hasPrevious()) { 46 | this._cursor--; 47 | return this.current(); 48 | } 49 | }; 50 | 51 | /** 52 | * Сдвигает курсор в конец и отдает последний элемент массива 53 | * @returns {*} 54 | */ 55 | Iterator.prototype.last = function () { 56 | this._cursor = this._items.length - 1; 57 | return this.current(); 58 | }; 59 | 60 | /** 61 | * Сброс курсора в начальное положение 62 | */ 63 | Iterator.prototype.reset = function () { 64 | this._cursor = 0; 65 | }; 66 | 67 | /** 68 | * Проверяет есть ли после текущего элемента еще один элемент 69 | * @returns {boolean} Истина/ложь 70 | */ 71 | Iterator.prototype.hasNext = function () { 72 | return this._cursor < this._items.length; 73 | }; 74 | 75 | /** 76 | * Проверяет есть ли перед текущим элементом еще один элемент 77 | * @returns {boolean} Истина/ложь 78 | */ 79 | Iterator.prototype.hasPrevious = function () { 80 | return this._cursor > 0; 81 | }; 82 | 83 | /** 84 | * Проходит по массиву и для каждого из его элементов выполняет функцию callback 85 | * @param {function} callback Функция, которая будет выполнена для каждого элемента 86 | */ 87 | Iterator.prototype.each = function (callback) { 88 | var item = this.first(); 89 | for(; this.hasNext(); item = this.next()) { 90 | callback(item); 91 | } 92 | }; 93 | 94 | return Iterator; 95 | })(); 96 | -------------------------------------------------------------------------------- /patterns/behavioral/iterator/spec/iterator.spec.js: -------------------------------------------------------------------------------- 1 | describe('Итератор / Iterator или Курсор / Cursor', function () { 2 | 3 | var iterated, arr, sum; 4 | 5 | before(function () { 6 | arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 7 | iterated = new Iterator(arr); 8 | sum = 0; 9 | }); 10 | 11 | describe('Проверка объекта', function () { 12 | 13 | it("Инстанс", function () { 14 | expect(iterated).to.be.an.instanceOf(Iterator); 15 | }); 16 | 17 | it('Методы', function () { 18 | expect(iterated.current).to.be.a('function'); 19 | expect(iterated.first).to.be.a('function'); 20 | expect(iterated.last).to.be.a('function'); 21 | expect(iterated.next).to.be.a('function'); 22 | expect(iterated.previous).to.be.a('function'); 23 | expect(iterated.reset).to.be.a('function'); 24 | expect(iterated.hasNext).to.be.a('function'); 25 | expect(iterated.hasPrevious).to.be.a('function'); 26 | expect(iterated.each).to.be.a('function'); 27 | }); 28 | }); 29 | 30 | describe('Работа методов', function () { 31 | it('Текущий элемент', function () { 32 | expect(iterated.current()).to.equal(0); 33 | }); 34 | 35 | it("Следующий элемент", function () { 36 | expect(iterated.next()).to.equal(1); 37 | }); 38 | 39 | it("Последний элемент", function () { 40 | expect(iterated.last()).to.equal(9); 41 | }); 42 | 43 | it("Предыдущий элемент", function () { 44 | expect(iterated.previous()).to.equal(8); 45 | }); 46 | 47 | it("Проход по всем элементам", function () { 48 | iterated.reset(); 49 | 50 | iterated.each(function (item) { 51 | sum += item; 52 | }); 53 | 54 | expect(sum).to.equal(45); 55 | }); 56 | 57 | it("Есть ли следующий элемент", function () { 58 | iterated.first(); 59 | 60 | expect(iterated.hasNext()).to.be.true; 61 | }); 62 | 63 | it("Есть ли предыдущий элемент", function () { 64 | iterated.last(); 65 | 66 | expect(iterated.hasPrevious()).to.be.true; 67 | }); 68 | 69 | it("Сброс курсора", function () { 70 | iterated.reset(); 71 | 72 | expect(iterated.current()).to.equal(0); 73 | }); 74 | 75 | it("Выход из диапазона справа", function () { 76 | iterated.last(); 77 | 78 | expect(iterated.next()).to.be.undefined; 79 | }); 80 | 81 | it("Выход из диапазона слева", function () { 82 | iterated.first(); 83 | 84 | expect(iterated.previous()).to.be.undefined; 85 | }); 86 | }); 87 | }); -------------------------------------------------------------------------------- /patterns/behavioral/observer/spec/observer.spec.js: -------------------------------------------------------------------------------- 1 | describe('Наблюдатель / Observer / Publisher-Subscriber', function () { 2 | 3 | var observer; 4 | 5 | describe('Проверка объекта', function () { 6 | 7 | before(function () { 8 | observer = new Observer(); 9 | }); 10 | 11 | it('Инстанс', function () { 12 | expect(observer).to.be.instanceOf(Observer); 13 | }); 14 | 15 | it('Методы', function() { 16 | expect(observer.subscribe).to.be.a('function'); 17 | expect(observer.publish).to.be.a('function'); 18 | expect(observer.unsubscribe).to.be.a('function'); 19 | }); 20 | }); 21 | 22 | describe('Поведение', function() { 23 | 24 | var firstFn, secondFn, thirdFn; 25 | 26 | beforeEach(function () { 27 | observer = new Observer(); 28 | firstFn = sinon.spy(); 29 | secondFn = sinon.spy(); 30 | thirdFn = sinon.spy(); 31 | }); 32 | 33 | it('Добавление обработчиков', function () { 34 | observer 35 | .subscribe(firstFn) 36 | .subscribe(secondFn) 37 | .subscribe(thirdFn) 38 | .publish(); 39 | 40 | expect(firstFn.called).to.be.true; 41 | expect(secondFn.called).to.be.true; 42 | expect(thirdFn.called).to.be.true; 43 | }); 44 | 45 | it('Добавление обработчика с данными', function () { 46 | 47 | var obj = { text: 'Hello' }; 48 | 49 | observer 50 | .subscribe(firstFn) 51 | .subscribe(secondFn) 52 | .subscribe(thirdFn) 53 | .publish(obj); 54 | 55 | expect(firstFn.calledWith(obj)).to.be.true; 56 | expect(secondFn.calledWith(obj)).to.be.true; 57 | expect(thirdFn.calledWith(obj)).to.be.true; 58 | }); 59 | 60 | it('Удаление из списка обработчиков', function () { 61 | 62 | observer 63 | .subscribe(firstFn) 64 | .subscribe(secondFn) 65 | .reset() 66 | .subscribe(thirdFn) 67 | .publish(); 68 | 69 | expect(firstFn.called).to.be.false; 70 | expect(secondFn.called).to.be.false; 71 | expect(thirdFn.called).to.be.true; 72 | }); 73 | 74 | it('Очистка списка обработчиков', function () { 75 | 76 | observer 77 | .subscribe(firstFn) 78 | .subscribe(secondFn) 79 | .subscribe(thirdFn) 80 | .unsubscribe(firstFn) 81 | .publish(); 82 | 83 | expect(firstFn.called).to.be.false; 84 | expect(secondFn.called).to.be.true; 85 | expect(thirdFn.called).to.be.true; 86 | }); 87 | }); 88 | }); -------------------------------------------------------------------------------- /patterns/structural/proxy/proxy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Класс, который будет защищать Proxy 3 | * */ 4 | var Calc = (function () { 5 | 6 | function Calc() { 7 | /** 8 | * Инициализация 9 | * */ 10 | } 11 | 12 | Calc.prototype.add = function (x, y) { 13 | return x + y; 14 | }; 15 | 16 | Calc.prototype.subtract = function (x, y) { 17 | return x - y; 18 | }; 19 | 20 | Calc.prototype.multiply = function (x, y) { 21 | return x * y; 22 | }; 23 | 24 | Calc.prototype.divide = function (x, y) { 25 | return x / y; 26 | }; 27 | 28 | Calc.prototype.power = function (x, y) { 29 | return Math.pow(x, y); 30 | }; 31 | 32 | Calc.prototype.root = function (x, y) { 33 | return this.power(x, this.divide(1, y)); 34 | }; 35 | 36 | return Calc; 37 | })(); 38 | 39 | /** 40 | * Класс, который выступает в роли суррогата класса Calc 41 | * */ 42 | var CalcProxy = (function () { 43 | 44 | var calc; 45 | 46 | function Proxy() { 47 | calc = null; 48 | } 49 | 50 | /** 51 | * Экземпляр calc будет недоступен из вне. 52 | * Он хранится в замыкании. 53 | * Будет инициализирован только при первом обращении. 54 | * */ 55 | function initialize() { 56 | if (calc === null) { 57 | calc = new Calc(); 58 | } 59 | } 60 | 61 | function parse(value) { 62 | value = parseInt(value, 10); 63 | if (isNaN(value)) { 64 | throw new Error('Arguments are invalid!'); 65 | } else { 66 | return value; 67 | } 68 | } 69 | 70 | Proxy.prototype.add = function (x, y) { 71 | initialize(); 72 | return calc.add(parse(x), parse(y)); 73 | }; 74 | 75 | Proxy.prototype.subtract = function (x, y) { 76 | initialize(); 77 | return calc.subtract(parse(x), parse(y)); 78 | }; 79 | 80 | Proxy.prototype.multiply = function (x, y) { 81 | initialize(); 82 | return calc.multiply(parse(x), parse(y)); 83 | }; 84 | 85 | Proxy.prototype.divide = function (x, y) { 86 | initialize(); 87 | y = parse(y); 88 | 89 | if (y === 0) { 90 | throw new Error('Division by zero!'); 91 | } 92 | 93 | return calc.divide(parse(x), y); 94 | }; 95 | 96 | Proxy.prototype.power = function (x, y) { 97 | initialize(); 98 | return calc.power(parse(x), parse(y)); 99 | }; 100 | 101 | Proxy.prototype.root = function (x, y) { 102 | initialize(); 103 | y = parse(y); 104 | 105 | if (y < 0) { 106 | throw new Error('Negative power!'); 107 | } 108 | 109 | return calc.root(parse(x), y); 110 | }; 111 | 112 | return Proxy; 113 | })(); -------------------------------------------------------------------------------- /patterns/structural/mixin/spec/mixin.spec.js: -------------------------------------------------------------------------------- 1 | describe('Примесь / Mixin', function () { 2 | 3 | var literalBook, functionalBook; 4 | 5 | before(function () { 6 | literalBook = new BookLiteral('Альбер Камю', 'Чума'); 7 | functionalBook = new BookFunctional('Герберт Уэлсс', 'Война миров'); 8 | }); 9 | 10 | describe('Инстанс', function () { 11 | 12 | it('BookLiteral', function () { 13 | expect(literalBook).to.be.an('object'); 14 | expect(literalBook).to.be.instanceOf(BookLiteral); 15 | expect(literalBook.getInfo).to.be.a('function'); 16 | }); 17 | 18 | it('BookFunctional', function () { 19 | expect(functionalBook).to.be.an('object'); 20 | expect(functionalBook).to.be.instanceOf(BookFunctional); 21 | expect(functionalBook.getInfo).to.be.a('function'); 22 | }); 23 | 24 | it('BookLiteralMixin', function () { 25 | expect(BookLiteralMixin).to.be.an('object'); 26 | expect(BookLiteralMixin.getAuthor).to.be.a('function'); 27 | expect(BookLiteralMixin.getTitle).to.be.a('function'); 28 | }); 29 | 30 | it('BookFunctionalMixin', function () { 31 | expect(BookFunctionalMixin).to.be.an('function'); 32 | }); 33 | 34 | }); 35 | 36 | describe('Поведение', function () { 37 | 38 | describe('Примесь - литерал', function () { 39 | 40 | it('Объект без примеси', function () { 41 | expect(literalBook.getAuthor).to.be.undefined; 42 | expect(literalBook.getTitle).to.be.undefined; 43 | }); 44 | 45 | it('Объект с примесью', function () { 46 | _.extend(BookLiteral.prototype, BookLiteralMixin); 47 | literalBook = new BookLiteral('Альбер Камю', 'Чума'); 48 | 49 | expect(literalBook.getAuthor).to.be.a('function'); 50 | expect(literalBook.getAuthor()).to.equal('Альбер Камю'); 51 | expect(literalBook.getTitle).to.be.a('function'); 52 | expect(literalBook.getTitle()).to.equal('Чума'); 53 | }); 54 | 55 | }); 56 | 57 | describe('Примесь - функция', function () { 58 | 59 | it('Объект без примеси', function () { 60 | expect(functionalBook.getAuthor).to.be.undefined; 61 | expect(functionalBook.getTitle).to.be.undefined; 62 | }); 63 | 64 | it('Объект с примесью', function () { 65 | BookFunctionalMixin.call(BookFunctional.prototype); 66 | functionalBook = new BookFunctional('Герберт Уэлсс', 'Война миров'); 67 | 68 | expect(functionalBook.getAuthor).to.be.a('function'); 69 | expect(functionalBook.getAuthor()).to.equal('Герберт Уэлсс'); 70 | expect(functionalBook.getTitle).to.be.a('function'); 71 | expect(functionalBook.getTitle()).to.equal('Война миров'); 72 | }); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /patterns/enterprise/registry/registry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Простой реестр, множество копий 3 | * */ 4 | var SimpleRegistry = (function () { 5 | 6 | /** 7 | * Создает экземпляр 8 | * @constructor 9 | */ 10 | function Registry() { 11 | /** Место, где будут храниться записи */ 12 | this._storage = {}; 13 | } 14 | 15 | /** 16 | * Вносит запись в реестр 17 | * @param {string} key Ключ записи реестра 18 | * @param {*} value Значение записи реестра 19 | */ 20 | Registry.prototype.set = function (key, value) { 21 | this._storage[key] = value; 22 | }; 23 | 24 | /** 25 | * Возвращает запись из реестра по ключу 26 | * @param {string} key Ключ записи реестра 27 | * @returns {*} Значение записи реестра 28 | */ 29 | Registry.prototype.get = function (key) { 30 | return this._storage[key]; 31 | }; 32 | 33 | /** 34 | * Проверяет существование записи в реестре 35 | * @param {string} key Ключ записи в реестре 36 | * @returns {boolean} Истина/ложь 37 | */ 38 | Registry.prototype.has = function (key) { 39 | return this._storage[key] !== undefined; 40 | }; 41 | 42 | return Registry; 43 | })(); 44 | 45 | /** 46 | * Глобальный реестр, то есть реестр-синглтон 47 | * */ 48 | var SingletonRegistry = (function () { 49 | 50 | var _instance, _storage; 51 | 52 | /** 53 | * Создает экземпляр реестра-синглтона 54 | * @constructor 55 | */ 56 | function Registry() { 57 | if (!_instance) { 58 | _storage = {}; 59 | _instance = this; 60 | } else { 61 | return _instance; 62 | } 63 | } 64 | /** 65 | * Дополнительно в конструктор можно передать 66 | * какое то значение по умолчанию, которое 67 | * отдавалось бы в случае обращения по 68 | * несуществующему ключу. В нашем же варианте 69 | * будет возвращаться undefined 70 | * */ 71 | 72 | /** 73 | * Вносит запись в реестр 74 | * @param {string} key Ключ записи реестра 75 | * @param {*} value Значение записи реестра 76 | */ 77 | Registry.prototype.set = function (key, value) { 78 | _storage[key] = value; 79 | }; 80 | 81 | /** 82 | * Возвращает запись из реестра по ключу 83 | * @param {string} key Ключ записи реестра 84 | * @returns {*} Значение записи реестра 85 | */ 86 | Registry.prototype.get = function (key) { 87 | return _storage[key]; 88 | }; 89 | 90 | /** 91 | * Проверяет существование записи в реестре 92 | * @param {string} key Ключ записи в реестре 93 | * @returns {boolean} Истина/ложь 94 | */ 95 | Registry.prototype.has = function (key) { 96 | return _storage[key] !== undefined; 97 | }; 98 | 99 | /** 100 | * В качестве дополнительного функционала можно добавить 101 | * метод reset() для очистки данных репозитория и 102 | * метод remove() для удаления какого конкретного значения 103 | * */ 104 | 105 | return Registry; 106 | })(); -------------------------------------------------------------------------------- /patterns/structural/proxy/spec/proxy.spec.js: -------------------------------------------------------------------------------- 1 | describe('Заместитель / Proxy или Суррогат / Surrogate', function () { 2 | 3 | var proxy, calc; 4 | 5 | before(function () { 6 | calc = new Calc(); 7 | proxy = new CalcProxy(); 8 | }); 9 | 10 | describe('Calc', function () { 11 | 12 | it('Инстанс', function () { 13 | expect(Calc).to.be.a('function'); 14 | expect(calc).to.be.an.instanceOf(Calc); 15 | }); 16 | 17 | describe('Методы', function () { 18 | 19 | it('add', function () { 20 | expect(calc.add).to.be.a('function'); 21 | expect(calc.add(2, 2)).to.equal(4); 22 | }); 23 | 24 | it('subtract', function () { 25 | expect(calc.subtract).to.be.a('function'); 26 | expect(calc.subtract(8, 2)).to.equal(6); 27 | }); 28 | 29 | it('multiply', function () { 30 | expect(calc.multiply).to.be.a('function'); 31 | expect(calc.multiply(8, 2)).to.equal(16); 32 | }); 33 | 34 | 35 | it('divide', function () { 36 | expect(calc.divide).to.be.a('function'); 37 | expect(calc.divide(4, 2)).to.equal(2); 38 | }); 39 | 40 | 41 | it('power', function () { 42 | expect(calc.power).to.be.a('function'); 43 | expect(calc.power(8, 2)).to.equal(64); 44 | }); 45 | 46 | it('root', function () { 47 | expect(calc.root).to.be.a('function'); 48 | expect(calc.root(16, 2)).to.equal(4); 49 | }); 50 | }); 51 | }); 52 | 53 | describe('CalcProxy', function () { 54 | 55 | it('Инстанс', function () { 56 | expect(CalcProxy).to.be.a('function'); 57 | expect(proxy).to.be.an.instanceOf(CalcProxy); 58 | }); 59 | 60 | describe('Методы', function () { 61 | 62 | it('add', function () { 63 | expect(proxy.add).to.be.a('function'); 64 | expect(proxy.add('2x2', 2)).to.equal(4); 65 | }); 66 | 67 | it('subtract', function () { 68 | expect(proxy.subtract).to.be.a('function'); 69 | expect(proxy.subtract(8, 2)).to.equal(6); 70 | }); 71 | 72 | it('multiply', function () { 73 | expect(proxy.multiply).to.be.a('function'); 74 | expect(proxy.multiply('08', 2)).to.equal(16); 75 | }); 76 | 77 | 78 | it('divide', function () { 79 | expect(proxy.divide).to.be.a('function'); 80 | expect(proxy.divide(4, 2)).to.equal(2); 81 | }); 82 | 83 | 84 | it('power', function () { 85 | expect(proxy.power).to.be.a('function'); 86 | expect(proxy.power(8, '2 ')).to.equal(64); 87 | }); 88 | 89 | it('root', function () { 90 | expect(proxy.root).to.be.a('function'); 91 | expect(proxy.root('16', 2)).to.equal(4); 92 | }); 93 | }); 94 | }); 95 | }); -------------------------------------------------------------------------------- /patterns/creational/singleton/spec/singleton.spec.js: -------------------------------------------------------------------------------- 1 | describe('Синглтон / Singleton', function () { 2 | 3 | var firstIns, secondIns; 4 | 5 | describe('Экземпляр хранится в замыкании, инстанс создается статическим методом', function () { 6 | before(function () { 7 | firstIns = ClosureStaticSingleton.getInstance(); 8 | secondIns = ClosureStaticSingleton.getInstance(); 9 | 10 | firstIns.toString = function() { 11 | return '[object ClosureStaticSingleton]'; 12 | }; 13 | }); 14 | 15 | it('Метод, возвращающий инстанс синглтона должен существовать', function () { 16 | expect(ClosureStaticSingleton.getInstance).to.exist; 17 | }); 18 | 19 | it('Обе переменные ссылаются на один и тот же объект', function () { 20 | expect(firstIns).to.equal(secondIns); 21 | }); 22 | 23 | it('Метода toString должен существовать', function () { 24 | expect(secondIns.toString).to.exist; 25 | }); 26 | 27 | it('Метод toString должен вернуть [object ClosureStaticSingleton]', function () { 28 | expect(secondIns.toString()).to.equal('[object ClosureStaticSingleton]'); 29 | }); 30 | }); 31 | 32 | describe('Экземпляр хранится в замыкании, инстанс создается конструктором', function () { 33 | before(function () { 34 | firstIns = new ClosureConstructorSingleton(); 35 | secondIns = new ClosureConstructorSingleton(); 36 | }); 37 | 38 | it('Метод, возвращающий инстанс синглтона должен существовать', function () { 39 | expect(ClosureConstructorSingleton).to.be.a('function'); 40 | }); 41 | 42 | it('Обе переменные ссылаются на один и тот же объект', function () { 43 | expect(firstIns === secondIns).to.equal(true); 44 | }); 45 | 46 | it('Метода toString должен существовать', function () { 47 | expect(secondIns.toString).to.exist; 48 | }); 49 | 50 | it('Метод toString должен вернуть [object ClosureConstructorSingleton]', function () { 51 | expect(secondIns.toString()).to.equal('[object ClosureConstructorSingleton]'); 52 | }); 53 | }); 54 | 55 | describe('Экземпляр хранится в статическом свойстве, инстанс создается конструктором', function () { 56 | before(function () { 57 | firstIns = new StaticConstructorSingleton(); 58 | secondIns = new StaticConstructorSingleton(); 59 | }); 60 | 61 | it('Метод, возвращающий инстанс синглтона должен существовать', function () { 62 | expect(StaticConstructorSingleton).to.be.a('function'); 63 | }); 64 | 65 | it('Обе переменные ссылаются на один и тот же объект', function () { 66 | expect(firstIns === secondIns).to.equal(true); 67 | }); 68 | 69 | it('Метода toString должен существовать', function () { 70 | expect(secondIns.toString).to.exist; 71 | }); 72 | 73 | it('Метод toString должен вернуть [object StaticConstructorSingleton]', function () { 74 | expect(secondIns.toString()).to.equal('[object StaticConstructorSingleton]'); 75 | }); 76 | }); 77 | }); -------------------------------------------------------------------------------- /patterns/behavioral/memento/memento.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Originator 3 | * */ 4 | var Person = (function () { 5 | 6 | /** 7 | * Создает экземпляр человека 8 | * @param {string} name Имя человека 9 | * @param {string} address Адрес местоположения человека 10 | * @constructor 11 | */ 12 | function Person(name, address) { 13 | this.name = name; 14 | this.address = address; 15 | } 16 | 17 | /** 18 | * Меняет местоположение человека 19 | * @param {string} newAddress Новое местоположение человека 20 | */ 21 | Person.prototype.move = function(newAddress) { 22 | this.address = newAddress; 23 | }; 24 | 25 | /** 26 | * Возвращает текущее местопложение человека 27 | * @returns {string} 28 | */ 29 | Person.prototype.whereami = function () { 30 | return this.address; 31 | }; 32 | 33 | /** 34 | * Сохраняет текущее состояние человека в хранитель 35 | * @returns {Memento} 36 | */ 37 | Person.prototype.save = function () { 38 | return new Memento(this); 39 | }; 40 | 41 | /** 42 | * Откатывает состояние человека до состояния, которое хранится в memento 43 | * @param memento 44 | */ 45 | Person.prototype.restore = function (memento) { 46 | if (memento === undefined) { 47 | throw new Error('Saved state is not found!'); 48 | } 49 | var state = JSON.parse(memento.getState()); 50 | 51 | this.name = state.name; 52 | this.address = state.address; 53 | }; 54 | 55 | return Person; 56 | })(); 57 | 58 | /** 59 | * Memento 60 | * */ 61 | var Memento = (function () { 62 | 63 | /** 64 | * Создает экземпляр мементо 65 | * @param {*} state Состояние 66 | * @constructor 67 | */ 68 | function Memento(state) { 69 | this.jsonState = JSON.stringify(state); 70 | } 71 | 72 | /** 73 | * Возвращает состояние 74 | * @returns {*} 75 | */ 76 | Memento.prototype.getState = function () { 77 | return this.jsonState; 78 | }; 79 | 80 | return Memento; 81 | })(); 82 | 83 | /** 84 | * Caretaker / 85 | * */ 86 | var Caretaker = (function (){ 87 | 88 | /** 89 | * Создает экземпляр класса 90 | * @constructor 91 | */ 92 | function Caretaker() { 93 | this.mementos = []; 94 | } 95 | 96 | /** 97 | * Сохраняет переданное в него состояние 98 | * @param {string} memento Состояние 99 | */ 100 | Caretaker.prototype.set = function(memento) { 101 | this.mementos.push(memento); 102 | }; 103 | 104 | /** 105 | * Возвращает сохраненное состояние по его индексу 106 | * @param {number} index Индекс сохраненного состояния 107 | * @returns {*} 108 | */ 109 | Caretaker.prototype.get = function(index) { 110 | var ind = parseInt(index, 10); 111 | return this.mementos[isNaN(ind) ? this.mementos.length - 1 : ind]; 112 | }; 113 | 114 | /** 115 | * Очищает список сохраненных состояний 116 | */ 117 | Caretaker.prototype.clean = function () { 118 | this.mementos = []; 119 | }; 120 | 121 | return Caretaker; 122 | })(); -------------------------------------------------------------------------------- /patterns/behavioral/state/state.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Калькулятор - Состояние 3 | */ 4 | var Calculator = (function () { 5 | 6 | /** 7 | * Создает экземпляр 8 | * @param {object} state Состояние 9 | * @constructor 10 | */ 11 | function Calculator(state) { 12 | /** 13 | * Хранит в себе текущий объект-состояние, методы которого будут вызываться 14 | * @type {Object} 15 | * @private 16 | */ 17 | this._state = state; 18 | } 19 | 20 | /** 21 | * Устанавливает состояние 22 | * @param {object} state Состояние 23 | */ 24 | Calculator.prototype.set = function (state) { 25 | this._state = state; 26 | }; 27 | 28 | /** 29 | * Вызывает метод execute() у текущего состояния 30 | * @returns {*|number} 31 | */ 32 | Calculator.prototype.execute = function () { 33 | return this._state.execute.apply(this, arguments); 34 | }; 35 | 36 | return Calculator; 37 | })(); 38 | 39 | /** 40 | * Класс, который выполняет сложение 41 | * */ 42 | var Add = (function () { 43 | 44 | /** 45 | * Создает экземпляр 46 | * @constructor 47 | */ 48 | function Add() { 49 | } 50 | 51 | /** 52 | * Складывает два операнда 53 | * @param {number} x Первый операнд 54 | * @param {number} y Второй операнд 55 | * @returns {number} Сумма 56 | */ 57 | Add.prototype.execute = function (x, y) { 58 | return x + y; 59 | }; 60 | 61 | return Add; 62 | })(); 63 | 64 | /** 65 | * Класс, который выполняет вычитание 66 | * */ 67 | var Subtract = (function () { 68 | 69 | /** 70 | * Создает экземпляр 71 | * @constructor 72 | */ 73 | function Subtract() { 74 | } 75 | 76 | /** 77 | * Вычитает из одного операнда другой 78 | * @param {number} x Уменьшаемое 79 | * @param {number} y Вычитаемое 80 | * @returns {number} Разница 81 | */ 82 | Subtract.prototype.execute = function (x, y) { 83 | return x - y; 84 | }; 85 | 86 | return Subtract; 87 | })(); 88 | 89 | /** 90 | * Класс, который выполняет умножение 91 | * */ 92 | var Multiply = (function () { 93 | 94 | /** 95 | * Создает экземпляр 96 | * @constructor 97 | */ 98 | function Multiply() { 99 | } 100 | 101 | /** 102 | * Перемножает два операнда 103 | * @param {number} x Первый операнд 104 | * @param {number} y Второй операнд 105 | * @returns {number} Произведение 106 | */ 107 | Multiply.prototype.execute = function (x, y) { 108 | return x * y; 109 | }; 110 | 111 | return Multiply; 112 | })(); 113 | 114 | /** 115 | * Класс, который выполняет деление 116 | * */ 117 | var Divide = (function () { 118 | 119 | /** 120 | * Создает экземпляр 121 | * @constructor 122 | */ 123 | function Divide() { 124 | } 125 | 126 | /** 127 | * Делит один операнд на другой 128 | * @param {number} x Делимое 129 | * @param {number} y Делитель 130 | * @returns {number} Результат 131 | */ 132 | Divide.prototype.execute = function (x, y) { 133 | return x / y; 134 | }; 135 | 136 | return Divide; 137 | })(); -------------------------------------------------------------------------------- /patterns/structural/adapter/spec/adapter.spec.js: -------------------------------------------------------------------------------- 1 | describe('Адаптер / Adapter', function () { 2 | 3 | var english, italian, adapter; 4 | 5 | before(function () { 6 | english = new Englishman('John', 'Smith'); 7 | italian = new Italian('Silvio', 'Berlusconi'); 8 | adapter = new AdaptToEnglish(italian); 9 | }); 10 | 11 | describe('Englishman', function () { 12 | 13 | it('Инстанс', function () { 14 | expect(Englishman).to.be.a('function'); 15 | expect(english).to.be.an.instanceOf(Englishman); 16 | }); 17 | 18 | describe('Методы', function () { 19 | 20 | it('getFirstname()', function () { 21 | expect(english.getFirstname).to.be.a('function'); 22 | expect(english.getFirstname()).to.equal('John'); 23 | }); 24 | 25 | it('getLastname()', function () { 26 | expect(english.getLastname).to.be.a('function'); 27 | expect(english.getLastname()).to.equal('Smith'); 28 | }); 29 | 30 | it('introduce()', function () { 31 | expect(english.introduce).to.be.a('function'); 32 | expect(english.introduce()).to.equal('My name is John Smith'); 33 | }); 34 | 35 | it('greet()', function () { 36 | expect(english.greet).to.be.a('function'); 37 | expect(english.greet()).to.equal('Hello! How are you?'); 38 | }); 39 | }); 40 | }); 41 | 42 | describe('Italian', function () { 43 | 44 | it('Инстанс', function () { 45 | expect(Italian).to.be.a('function'); 46 | expect(italian).to.be.an.instanceOf(Italian); 47 | }); 48 | 49 | describe('Методы', function () { 50 | 51 | it('getFirstname()', function () { 52 | expect(italian.getFirstname).to.be.a('function'); 53 | expect(italian.getFirstname()).to.equal('Silvio'); 54 | }); 55 | 56 | it('getLastname()', function () { 57 | expect(italian.getLastname).to.be.a('function'); 58 | expect(italian.getLastname()).to.equal('Berlusconi'); 59 | }); 60 | 61 | it('introdurre()', function () { 62 | expect(italian.introdurre).to.be.a('function'); 63 | expect(italian.introdurre()).to.equal('Il mio nome è Silvio Berlusconi'); 64 | }); 65 | 66 | it('greet()', function () { 67 | expect(italian.ciao).to.be.a('function'); 68 | expect(italian.ciao()).to.equal('Ciao! Come stai?'); 69 | }); 70 | }); 71 | }); 72 | 73 | describe('AdaptToEnglish', function () { 74 | 75 | it('Инстанс', function () { 76 | expect(AdaptToEnglish).to.be.a('function'); 77 | expect(adapter).to.be.an.instanceOf(AdaptToEnglish); 78 | }); 79 | 80 | describe('Методы', function () { 81 | 82 | it('introduce()', function () { 83 | expect(adapter.introduce).to.be.a('function'); 84 | expect(adapter.introduce()).to.equal('My name is Silvio Berlusconi'); 85 | }); 86 | 87 | it('greet()', function () { 88 | expect(adapter.greet).to.be.a('function'); 89 | expect(adapter.greet()).to.equal('Hello! How are you?'); 90 | }); 91 | }); 92 | }); 93 | }); -------------------------------------------------------------------------------- /patterns/enterprise/reqres/spec/reqres.spec.js: -------------------------------------------------------------------------------- 1 | describe('Запрос/Ответ / Request/Response', function () { 2 | 3 | var reqres, server, client; 4 | 5 | before(function () { 6 | reqres = new RequestResponse(); 7 | server = new Server(reqres); 8 | client = new Client(reqres); 9 | }); 10 | 11 | describe('Request/Response', function () { 12 | 13 | it('RequestResponse является конструктором', function () { 14 | expect(RequestResponse).to.be.a('function'); 15 | expect(reqres).to.be.an.instanceOf(RequestResponse); 16 | }); 17 | 18 | it('RequestResponse обладает методом setResponse()', function() { 19 | expect(reqres.setResponse).to.be.a('function'); 20 | }); 21 | 22 | it('RequestResponse обладает методом removeResponse()', function() { 23 | expect(reqres.removeResponse).to.be.a('function'); 24 | }); 25 | 26 | it('RequestResponse обладает методом request()', function() { 27 | expect(reqres.request).to.be.a('function'); 28 | }); 29 | 30 | it('RequestResponse обладает методом clean()', function() { 31 | expect(reqres.clean).to.be.a('function'); 32 | }); 33 | }); 34 | 35 | describe('Client и Server', function () { 36 | 37 | it('Client является конструктором', function () { 38 | expect(Client).to.be.a('function'); 39 | expect(client).to.be.an.instanceOf(Client); 40 | }); 41 | 42 | it('Client обладает методом add()', function() { 43 | expect(client.add).to.be.a('function'); 44 | }); 45 | 46 | it('Client обладает методом subtract()', function() { 47 | expect(client.subtract).to.be.a('function'); 48 | }); 49 | 50 | it('Client обладает методом multiply()', function() { 51 | expect(client.multiply).to.be.a('function'); 52 | }); 53 | 54 | it('Client обладает методом divide()', function() { 55 | expect(client.divide).to.be.a('function'); 56 | }); 57 | 58 | it('Server является конструктором', function () { 59 | expect(Server).to.be.a('function'); 60 | expect(server).to.be.an.instanceOf(Server); 61 | }); 62 | }); 63 | 64 | describe('Поведение', function () { 65 | 66 | it("Через client: 4 + 5 = 9", function () { 67 | expect(client.add(4, 5)).to.equal(9); 68 | }); 69 | 70 | it("Через reqres: 4 + 5 = 9", function () { 71 | expect(reqres.request('add', { x: 4, y: 5})).to.equal(9); 72 | }); 73 | 74 | it("Через client: 21 - 3 = 18", function () { 75 | expect(client.subtract(21, 3)).to.equal(18); 76 | }); 77 | 78 | it("Через reqres: 21 - 3 = 18", function () { 79 | expect(reqres.request('subtract', { x: 21, y: 3})).to.equal(18); 80 | }); 81 | 82 | it("Через client: 16 * 15 = 240", function () { 83 | expect(client.multiply(16, 15)).to.equal(240); 84 | }); 85 | 86 | it("Через reqres: 16 * 15 = 240", function () { 87 | expect(reqres.request('multiply', { x: 16, y: 15})).to.equal(240); 88 | }); 89 | 90 | it("Через client: 22 / 2 = 11", function () { 91 | expect(client.divide(22, 2)).to.equal(11); 92 | }); 93 | 94 | it("Через reqres: 22 / 2 = 11", function () { 95 | expect(reqres.request('divide', { x: 22, y: 2})).to.equal(11); 96 | }); 97 | }); 98 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Javascript Design Patterns (Черновик) 2 | 3 | ### Реализация на javascript шаблонов проектирования 4 | 5 | 1. Порождающие шаблоны / Creational patterns 6 | + Абстрактная фабрика / Abstract factory 7 | + [Пространство имен / Namespace] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/creational/namespace) 8 | + [Прототип / Prototype] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/creational/prototype) 9 | + [Синглтон / Одиночка / Singleton] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/creational/singleton) 10 | + Строитель / Builder 11 | + Фабричный метод / Factory method 12 | 2. Структурные шаблоны / Structural patterns 13 | + [Адаптер / Adapter] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/adapter) 14 | + [Декоратор / Decorator или Оболочка / Wrapper] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/decorator) 15 | + [Заместитель / Proxy или Суррогат / Surrogate] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/proxy) 16 | + [Компоновщик / Composite] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/composite) 17 | + [Модуль / Module] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/module) 18 | + Мост / Bridge 19 | + [Примесь / Mixin] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/mixin) 20 | + Приспособленец / Flyweight 21 | + [Фасад / Facade] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/structural/facade) 22 | 3. Поведенческие шаблоны / Behavioral patterns 23 | + Интерпретатор / Interpreter 24 | + [Итератор / Iterator или Курсор / Cursor] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/iterator) 25 | + Команда / Command, Действие / Action или Транзакция / Transaction 26 | + [Наблюдатель / Observer / Publisher-Subscriber] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/observer) 27 | + Посетитель / Visitor 28 | + [Посредник / Mediator] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/mediator) 29 | + [Состояние / State] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/state) 30 | + [Стратегия / Strategy] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/strategy) 31 | + [Хранитель / Memento] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/memento) 32 | + [Цепочка обязаностей / Chain of Responsibility] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/behavioral/chain-of-responsibility) 33 | + Шаблонный метод / Template Method 34 | 4. Шаблоны корпоративных приложений / Enterprise patterns 35 | + [Реестр / Registry] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/enterprise/registry) 36 | + Репозиторий / Repository 37 | + [Запрос/Ответ / Request/Response] (https://github.com/instanceofpro/javascript-design-patterns/tree/master/patterns/enterprise/reqres) 38 | 5. Прочие - отсортировать 39 | + Единая точка входа / Front Controller 40 | + Sandbox / Песочница 41 | + Объектный пул / Object pool 42 | + Отложенная инициализация / Lazy initialization -------------------------------------------------------------------------------- /patterns/structural/facade/spec/facade.spec.js: -------------------------------------------------------------------------------- 1 | describe('Фасад / Facade', function () { 2 | 3 | describe("Прверка инстансов", function () { 4 | 5 | var market, cash, cart, warehouse; 6 | 7 | before(function () { 8 | market = new Supermarket(['milk', 'bread', 'meat'], 100); 9 | cash = new Cash(100); 10 | cart = new Cart(); 11 | warehouse = new Warehouse({ 12 | milk: { count: 200, price: 6 }, 13 | bread: { count: 150, price: 5 }, 14 | meat: { count: 120, price: 20 } 15 | }); 16 | }); 17 | 18 | it("Supermarket", function () { 19 | expect(market).to.be.an.instanceOf(Supermarket); 20 | }); 21 | 22 | it("Cash", function () { 23 | expect(cash).to.be.an.instanceOf(Cash); 24 | }); 25 | 26 | it("Cart", function () { 27 | expect(cart).to.be.an.instanceOf(Cart); 28 | }); 29 | 30 | it("Warehouse", function () { 31 | expect(warehouse).to.be.an.instanceOf(Warehouse); 32 | }); 33 | }); 34 | 35 | describe('Cash', function () { 36 | 37 | var cash; 38 | 39 | before(function () { 40 | cash = new Cash(100); 41 | }); 42 | 43 | it('Cash.pay', function() { 44 | expect(cash.pay(200)).to.be.false; 45 | expect(cash.pay(90)).to.be.true; 46 | }); 47 | }); 48 | 49 | describe('Cart', function () { 50 | 51 | var cart; 52 | 53 | before(function () { 54 | cart = new Cart(); 55 | }); 56 | 57 | it('Cart.add', function () { 58 | cart 59 | .add(100) 60 | .add(100) 61 | .add(100) 62 | .add(100); 63 | 64 | expect(cart.getCost()).to.equal(400); 65 | }); 66 | 67 | it('Cart.clean', function () { 68 | cart.clean(); 69 | 70 | expect(cart.getCost()).to.equal(0); 71 | }); 72 | }); 73 | 74 | describe('Warehouse', function () { 75 | 76 | var count, warehouse; 77 | 78 | beforeEach(function () { 79 | warehouse = new Warehouse({ 80 | milk: { count: 200, price: 6 }, 81 | bread: { count: 150, price: 5 }, 82 | meat: { count: 120, price: 20 } 83 | }); 84 | 85 | count = warehouse.getItemCount('milk'); 86 | }); 87 | 88 | it('Warehouse.getItem', function () { 89 | expect(warehouse.getItem('milk')).to.equal(6); 90 | expect(count - warehouse.getItemCount('milk')).to.equal(1); 91 | }); 92 | 93 | it('Warehouse.getItemCount', function () { 94 | expect(warehouse.getItemCount('milk')).to.equal(200); 95 | expect(warehouse.getItemCount('bread')).to.equal(150); 96 | expect(warehouse.getItemCount('meat')).to.equal(120); 97 | }); 98 | }); 99 | 100 | describe('Supermarket', function () { 101 | 102 | var supermarket; 103 | 104 | before(function () { 105 | supermarket = new Supermarket(['milk', 'bread', 'meat'], 100, { 106 | milk: { count: 200, price: 6 }, 107 | bread: { count: 150, price: 5 }, 108 | meat: { count: 120, price: 20 } 109 | }); 110 | }); 111 | 112 | it('Supermarket.goShopping', function () { 113 | expect(supermarket.goShopping()).to.be.true; 114 | }); 115 | 116 | }); 117 | 118 | }); -------------------------------------------------------------------------------- /patterns/structural/module/module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Object Literal Module Pattern 3 | * Модуль в виде литерала объекта 4 | * */ 5 | var calculatorLiteral = { 6 | /** 7 | * Локальной области видимости нет, 8 | * потому нет возможности создавать приватные свойтсва 9 | * */ 10 | add: function (x, y) { 11 | return x + y; 12 | }, 13 | 14 | subtract: function (x, y) { 15 | return x - y; 16 | }, 17 | 18 | multiply: function (x, y) { 19 | return x * y; 20 | }, 21 | 22 | divide: function (x, y) { 23 | return x / y; 24 | }, 25 | 26 | PI: 3.1415926 27 | }; 28 | 29 | /** 30 | * Модуль в виде объекта, который возвращает функция 31 | * */ 32 | var calculatorFunc = (function () { 33 | /** 34 | * Приватные свойства 35 | * */ 36 | function abs(value) { 37 | return value > 0 ? value : -value; 38 | } 39 | 40 | /** 41 | * Публичные свойства 42 | * */ 43 | return { 44 | add: function (x, y) { 45 | return x + y; 46 | }, 47 | 48 | subtract: function (x, y) { 49 | /** 50 | * Публичный метод использует приватный метод, при этом приватный метод недоступен 51 | * */ 52 | return abs(x - y); 53 | }, 54 | 55 | multiply: function (x, y) { 56 | return x * y; 57 | }, 58 | 59 | divide: function (x, y) { 60 | return x / y; 61 | }, 62 | 63 | PI: 3.1415926 64 | } 65 | 66 | })(); 67 | 68 | /** 69 | * Revealing Module Pattern 70 | * Улучшенный вариант предыдущей реализации. 71 | * Все свойства приватны, в конце определения модуля 72 | * определяется каким свойствам открыть доступ 73 | * */ 74 | var calculatorReleaving = (function () { 75 | 76 | var PI = 3.1415926; 77 | 78 | /** 79 | * Приватные методы 80 | * */ 81 | function abs(value) { 82 | return value > 0 ? value : -value; 83 | } 84 | 85 | function add(x, y) { 86 | return x + y; 87 | } 88 | 89 | function subtract(x, y) { 90 | return abs(x - y); 91 | } 92 | 93 | function multiply(x, y) { 94 | return x * y; 95 | } 96 | 97 | function divide(x, y) { 98 | return x / y; 99 | } 100 | 101 | function ringLength(radius) { 102 | return 2 * PI * radius; 103 | } 104 | 105 | /** 106 | * Открытие доступа 107 | * */ 108 | return { 109 | add: add, 110 | subtract: subtract, 111 | multiply: multiply, 112 | divide: divide, 113 | ringLength: ringLength 114 | }; 115 | })(); 116 | 117 | /** 118 | * Модуль, возвращающий конструктор 119 | * */ 120 | var calculatorCtor = (function () { 121 | 122 | function Calculator() { 123 | /** 124 | * Инициализация 125 | * */ 126 | } 127 | 128 | /** 129 | * Приватные свойства 130 | * */ 131 | var PI = 3.1415926; 132 | 133 | function abs(value) { 134 | return value > 0 ? value : -value; 135 | } 136 | 137 | /** 138 | * Публичные свойства вносятся в прототип 139 | * */ 140 | Calculator.prototype.add = function (x, y) { 141 | return x + y; 142 | }; 143 | 144 | Calculator.prototype.subtract = function (x, y) { 145 | /** 146 | * Публичный метод использует приватный метод, при этом приватный метод недоступен 147 | * */ 148 | return abs(x - y); 149 | }; 150 | 151 | Calculator.prototype.multiply = function (x, y) { 152 | return x * y; 153 | }; 154 | 155 | Calculator.prototype.divide = function (x, y) { 156 | return x / y; 157 | }; 158 | 159 | /** 160 | * Возвращаем конструктор 161 | * */ 162 | return Calculator; 163 | })(); -------------------------------------------------------------------------------- /patterns/structural/module/spec/module.spec.js: -------------------------------------------------------------------------------- 1 | describe('Модуль / Module', function () { 2 | 3 | describe('Object Literal', function () { 4 | 5 | it('Инстанс', function () { 6 | expect(calculatorLiteral).to.be.an('object'); 7 | expect(calculatorLiteral).not.to.be.empty; 8 | }); 9 | 10 | it('Методы', function () { 11 | expect(calculatorLiteral.add).to.be.a('function'); 12 | expect(calculatorLiteral.add(2, 2)).to.equal(4); 13 | expect(calculatorLiteral.divide).to.be.a('function'); 14 | expect(calculatorLiteral.divide(4, 2)).to.equal(2); 15 | expect(calculatorLiteral.multiply).to.be.a('function'); 16 | expect(calculatorLiteral.multiply(8, 2)).to.equal(16); 17 | expect(calculatorLiteral.subtract).to.be.a('function'); 18 | expect(calculatorLiteral.subtract(8, 2)).to.equal(6); 19 | }); 20 | }); 21 | 22 | describe('Function', function () { 23 | 24 | it('Инстанс', function () { 25 | expect(calculatorFunc).to.be.an('object'); 26 | expect(calculatorFunc).not.to.be.empty; 27 | }); 28 | 29 | it('Публичные методы', function () { 30 | expect(calculatorFunc.add).to.be.a('function'); 31 | expect(calculatorFunc.add(2, 2)).to.equal(4); 32 | expect(calculatorFunc.divide).to.be.a('function'); 33 | expect(calculatorFunc.divide(4, 2)).to.equal(2); 34 | expect(calculatorFunc.multiply).to.be.a('function'); 35 | expect(calculatorFunc.multiply(8, 2)).to.equal(16); 36 | expect(calculatorFunc.subtract).to.be.a('function'); 37 | expect(calculatorFunc.subtract(8, 2)).to.equal(6); 38 | }); 39 | 40 | it('Приватный метод', function () { 41 | expect(calculatorFunc.abs).to.be.undefined; 42 | }); 43 | }); 44 | 45 | describe('Revealing Module', function () { 46 | 47 | it('Инстанс', function () { 48 | expect(calculatorReleaving).to.be.an('object'); 49 | expect(calculatorReleaving).not.to.be.empty; 50 | }); 51 | 52 | it('Публичные методы', function () { 53 | expect(calculatorReleaving.add).to.be.a('function'); 54 | expect(calculatorReleaving.add(2, 2)).to.equal(4); 55 | expect(calculatorReleaving.divide).to.be.a('function'); 56 | expect(calculatorReleaving.divide(4, 2)).to.equal(2); 57 | expect(calculatorReleaving.multiply).to.be.a('function'); 58 | expect(calculatorReleaving.multiply(8, 2)).to.equal(16); 59 | expect(calculatorReleaving.subtract).to.be.a('function'); 60 | expect(calculatorReleaving.subtract(8, 2)).to.equal(6); 61 | expect(calculatorReleaving.ringLength).to.be.a('function'); 62 | expect(calculatorReleaving.ringLength(2)).to.equal(12.5663704); 63 | }); 64 | 65 | it('Приватный метод', function () { 66 | expect(calculatorReleaving.abs).to.be.undefined; 67 | }); 68 | }); 69 | 70 | describe('Constructor', function () { 71 | 72 | var calc; 73 | 74 | before(function () { 75 | calc = new calculatorCtor(); 76 | }); 77 | 78 | it('Инстанс', function () { 79 | expect(calculatorCtor).to.be.a('function'); 80 | expect(calc).to.be.an.instanceOf(calculatorCtor); 81 | }); 82 | 83 | it('Публичные методы', function () { 84 | expect(calc.add).to.be.a('function'); 85 | expect(calc.add(2, 2)).to.equal(4); 86 | expect(calc.divide).to.be.a('function'); 87 | expect(calc.divide(4, 2)).to.equal(2); 88 | expect(calc.multiply).to.be.a('function'); 89 | expect(calc.multiply(8, 2)).to.equal(16); 90 | expect(calc.subtract).to.be.a('function'); 91 | expect(calc.subtract(8, 2)).to.equal(6); 92 | }); 93 | 94 | it('Приватный метод', function () { 95 | expect(calc.abs).to.be.undefined; 96 | }); 97 | }); 98 | }); -------------------------------------------------------------------------------- /patterns/behavioral/memento/spec/memento.spec.js: -------------------------------------------------------------------------------- 1 | describe('Хранитель / Memento', function () { 2 | 3 | var person, caretaker, mem; 4 | 5 | describe('Классы', function (){ 6 | 7 | before(function () { 8 | person = new Person('John', 'Moscow'); 9 | caretaker = new Caretaker(); 10 | mem = new Memento({hello: 'world'}); 11 | }); 12 | 13 | it('Person является классом', function () { 14 | expect(Person).to.be.a('function'); 15 | expect(person).to.be.an.instanceOf(Person); 16 | }); 17 | 18 | it('Экземпляр класса Person имеет метод move()', function() { 19 | expect(person.move).to.be.a('function'); 20 | }); 21 | 22 | it('Экземпляр класса Person имеет метод whereami()', function() { 23 | expect(person.whereami).to.be.a('function'); 24 | expect(person.whereami()).to.equal('Moscow'); 25 | }); 26 | 27 | it('Экземпляр класса Person имеет метод save()', function() { 28 | expect(person.save).to.be.a('function'); 29 | expect(person.save()).to.be.an.instanceOf(Memento); 30 | }); 31 | 32 | it('Экземпляр класса Person имеет метод restore()', function() { 33 | var save = person.save(); 34 | person.move('Tokyo'); 35 | person.restore(save); 36 | 37 | expect(person.restore).to.be.a('function'); 38 | expect(person.whereami()).to.equal('Moscow'); 39 | }); 40 | 41 | it('Memento является классом', function () { 42 | expect(Memento).to.be.a('function'); 43 | expect(mem).to.be.an.instanceOf(Memento); 44 | }); 45 | 46 | it('Экземпляр класса Memento имеет метод getState()', function() { 47 | expect(mem.getState).to.be.a('function'); 48 | expect(mem.getState()).to.equal('{"hello":"world"}'); 49 | }); 50 | 51 | it('Caretaker является классом', function () { 52 | expect(Caretaker).to.be.a('function'); 53 | expect(caretaker).to.be.an.instanceOf(Caretaker); 54 | }); 55 | 56 | it('Экземпляр класса Memento имеет метод set()', function() { 57 | expect(caretaker.set).to.be.a('function'); 58 | }); 59 | 60 | it('Экземпляр класса Memento имеет метод get()', function() { 61 | expect(caretaker.get).to.be.a('function'); 62 | }); 63 | 64 | it('Экземпляр класса Memento имеет метод clean()', function() { 65 | expect(caretaker.clean).to.be.a('function'); 66 | }); 67 | }); 68 | 69 | describe('Поведение', function () { 70 | 71 | beforeEach(function () { 72 | person = new Person('John', 'Moscow'); 73 | caretaker = new Caretaker(); 74 | }); 75 | 76 | it('Сохраненное и восстановленное состояния должны быть идентичными', function () { 77 | var oldAddress = person.whereami(); 78 | 79 | caretaker.set(person.save()); 80 | person.restore(caretaker.get(0)); 81 | 82 | var newAddress = person.whereami(); 83 | 84 | expect(oldAddress === newAddress).to.be.true; 85 | }); 86 | 87 | it('Множественное сохранение состояний', function () { 88 | 89 | caretaker.set(person.save()); 90 | 91 | person.move('New York'); 92 | caretaker.set(person.save()); 93 | 94 | person.move('Tokyo'); 95 | caretaker.set(person.save()); 96 | 97 | person.move('London'); 98 | caretaker.set(person.save()); 99 | 100 | person.move('Paris'); 101 | caretaker.set(person.save()); 102 | 103 | 104 | 105 | person.restore(caretaker.get(0)); 106 | expect(person.whereami()).to.equal('Moscow'); 107 | 108 | person.restore(caretaker.get(1)); 109 | expect(person.whereami()).to.equal('New York'); 110 | 111 | person.restore(caretaker.get(2)); 112 | expect(person.whereami()).to.equal('Tokyo'); 113 | 114 | person.restore(caretaker.get(3)); 115 | expect(person.whereami()).to.equal('London'); 116 | 117 | person.restore(caretaker.get()); 118 | expect(person.whereami()).to.equal('Paris'); 119 | 120 | }); 121 | 122 | }); 123 | 124 | }); 125 | -------------------------------------------------------------------------------- /patterns/enterprise/reqres/reqres.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Класс, который отвечает за передачу запроса 3 | * */ 4 | var RequestResponse = (function () { 5 | 6 | /** 7 | * Создает экземпляр 8 | * @constructor 9 | */ 10 | function Reqres () { 11 | /** 12 | * Хранилище запросов 13 | * @private 14 | * */ 15 | this._requests = {}; 16 | } 17 | 18 | /** 19 | * Задает обработчик запроса 20 | * @param {string} name Имя запроса 21 | * @param {function} handler Обработчик запроса 22 | * @param {*} context Контекст запроса 23 | * @returns {RequestResponse} 24 | */ 25 | Reqres.prototype.setResponse = function (name, handler, context) { 26 | 27 | if (!this._requests.hasOwnProperty(name)) { 28 | this._requests[name] = { 29 | fn: handler, 30 | context: context 31 | }; 32 | } else { 33 | throw new Error('The response already exists!'); 34 | } 35 | 36 | /** 37 | * На один запрос может быть только один ответ 38 | * */ 39 | return this; 40 | }; 41 | 42 | /** 43 | * Удаляет обработчик запроса 44 | * @param {string} name Имя запроса 45 | * @returns {RequestResponse} 46 | */ 47 | Reqres.prototype.removeResponse = function (name) { 48 | if (this._requests.hasOwnProperty(name)) { 49 | this._requests[name] = undefined; 50 | } 51 | 52 | return this; 53 | }; 54 | 55 | /** 56 | * Делает запрос. 57 | * @param {string} name Имя запроса 58 | * @param {*} data Данные, которые передаются в запрос 59 | * @returns {*} Результат выполнения обработчика запроса 60 | */ 61 | Reqres.prototype.request = function (name, data) { 62 | if (this._requests.hasOwnProperty(name)) { 63 | return this._requests[name].fn.call(this._requests[name].context, data); 64 | } else { 65 | throw new Error('The request does not exist!'); 66 | } 67 | }; 68 | 69 | /** 70 | * Очищает хранилище запросов 71 | */ 72 | Reqres.prototype.clean = function () { 73 | this._requests = {}; 74 | }; 75 | 76 | return Reqres; 77 | })(); 78 | 79 | /** 80 | * Сервер 81 | * */ 82 | var Server = (function () { 83 | 84 | /** 85 | * @param {RequestResponse} reqres Экзампляр хранителя запросов 86 | * @constructor 87 | * 88 | * В сервер инжектирован экзепмляр RequestResponse 89 | * Сервер не знает кто к нему обращается 90 | */ 91 | function Server(reqres) { 92 | this._reqres = reqres; 93 | 94 | /** 95 | * Задаем обработчики запросов 96 | * */ 97 | this._reqres 98 | .setResponse('add', function (data) { 99 | return data.x + data.y; 100 | }) 101 | .setResponse('subtract', function (data) { 102 | return data.x - data.y; 103 | }) 104 | .setResponse('multiply', function (data) { 105 | return data.x * data.y; 106 | }) 107 | .setResponse('divide', function (data) { 108 | return data.x / data.y; 109 | }); 110 | } 111 | 112 | return Server; 113 | })(); 114 | 115 | /** 116 | * Клиент 117 | * */ 118 | var Client = (function () { 119 | 120 | /** 121 | * @param {RequestResponse} reqres Экзампляр хранителя запросов 122 | * @constructor 123 | * 124 | * В клиент инжектирован экзепмляр RequestResponse 125 | * Клиент ничего не знает о сервере 126 | */ 127 | function Client(reqres) { 128 | this._reqres = reqres; 129 | } 130 | 131 | /** 132 | * Возвращает результат запроса на сложение двух чисел 133 | * @param {number} x Первый операнд 134 | * @param {number} y Второй операнд 135 | * @returns {*} Результат запроса 136 | */ 137 | Client.prototype.add = function(x, y) { 138 | return this._reqres.request('add', { x: x, y: y }); 139 | }; 140 | 141 | /** 142 | * Возвращает результат запроса на вычитание двух чисел 143 | * @param {number} x Уменьшаемое 144 | * @param {number} y Вычитаемое 145 | * @returns {*} Результат запроса 146 | */ 147 | Client.prototype.subtract = function(x, y) { 148 | return this._reqres.request('subtract', { x: x, y: y }); 149 | }; 150 | 151 | /** 152 | * Возвращает результат запроса на умножение двух чисел 153 | * @param {number} x Первый операнд 154 | * @param {number} y Второй операнд 155 | * @returns {*} Результат запроса 156 | */ 157 | Client.prototype.multiply = function(x, y) { 158 | return this._reqres.request('multiply', { x: x, y: y }); 159 | }; 160 | 161 | /** 162 | * Возвращает результат запроса на деление двух чисел 163 | * @param {number} x Делимое 164 | * @param {number} y Делитель 165 | * @returns {*} Результат запроса 166 | */ 167 | Client.prototype.divide = function(x, y) { 168 | return this._reqres.request('divide', { x: x, y: y }); 169 | }; 170 | 171 | return Client; 172 | })(); -------------------------------------------------------------------------------- /assets/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | /** 140 | * (1): approximate for browsers not supporting calc 141 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 142 | * ^^ seriously 143 | */ 144 | #mocha .test pre { 145 | display: block; 146 | float: left; 147 | clear: left; 148 | font: 12px/1.5 monaco, monospace; 149 | margin: 5px; 150 | padding: 15px; 151 | border: 1px solid #eee; 152 | max-width: 85%; /*(1)*/ 153 | max-width: calc(100% - 42px); /*(2)*/ 154 | word-wrap: break-word; 155 | border-bottom-color: #ddd; 156 | -webkit-border-radius: 3px; 157 | -webkit-box-shadow: 0 1px 3px #eee; 158 | -moz-border-radius: 3px; 159 | -moz-box-shadow: 0 1px 3px #eee; 160 | border-radius: 3px; 161 | } 162 | 163 | #mocha .test h2 { 164 | position: relative; 165 | } 166 | 167 | #mocha .test a.replay { 168 | position: absolute; 169 | top: 3px; 170 | right: 0; 171 | text-decoration: none; 172 | vertical-align: middle; 173 | display: block; 174 | width: 15px; 175 | height: 15px; 176 | line-height: 15px; 177 | text-align: center; 178 | background: #eee; 179 | font-size: 15px; 180 | -moz-border-radius: 15px; 181 | border-radius: 15px; 182 | -webkit-transition: opacity 200ms; 183 | -moz-transition: opacity 200ms; 184 | transition: opacity 200ms; 185 | opacity: 0.3; 186 | color: #888; 187 | } 188 | 189 | #mocha .test:hover a.replay { 190 | opacity: 1; 191 | } 192 | 193 | #mocha-report.pass .test.fail { 194 | display: none; 195 | } 196 | 197 | #mocha-report.fail .test.pass { 198 | display: none; 199 | } 200 | 201 | #mocha-report.pending .test.pass, 202 | #mocha-report.pending .test.fail { 203 | display: none; 204 | } 205 | #mocha-report.pending .test.pass.pending { 206 | display: block; 207 | } 208 | 209 | #mocha-error { 210 | color: #c00; 211 | font-size: 1.5em; 212 | font-weight: 100; 213 | letter-spacing: 1px; 214 | } 215 | 216 | #mocha-stats { 217 | position: fixed; 218 | top: 15px; 219 | right: 10px; 220 | font-size: 12px; 221 | margin: 0; 222 | color: #888; 223 | z-index: 1; 224 | } 225 | 226 | #mocha-stats .progress { 227 | float: right; 228 | padding-top: 0; 229 | } 230 | 231 | #mocha-stats em { 232 | color: black; 233 | } 234 | 235 | #mocha-stats a { 236 | text-decoration: none; 237 | color: inherit; 238 | } 239 | 240 | #mocha-stats a:hover { 241 | border-bottom: 1px solid #eee; 242 | } 243 | 244 | #mocha-stats li { 245 | display: inline-block; 246 | margin: 0 5px; 247 | list-style: none; 248 | padding-top: 11px; 249 | } 250 | 251 | #mocha-stats canvas { 252 | width: 40px; 253 | height: 40px; 254 | } 255 | 256 | #mocha code .comment { color: #ddd; } 257 | #mocha code .init { color: #2f6fad; } 258 | #mocha code .string { color: #5890ad; } 259 | #mocha code .keyword { color: #8a6343; } 260 | #mocha code .number { color: #2f6fad; } 261 | 262 | @media screen and (max-device-width: 480px) { 263 | #mocha { 264 | margin: 60px 0px; 265 | } 266 | 267 | #mocha #stats { 268 | position: absolute; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /patterns/behavioral/mediator/mediator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Медиатор с каналами 3 | * */ 4 | var Mediator = (function () { 5 | /** 6 | * Хранилище каналов. 7 | * Канал представляет собой объект с двумя свойствами: 8 | * handlers {array} - обработчики этого канала (fn {function} и context {object}) 9 | * nested {object} - вложенные каналы 10 | * */ 11 | var storage = {}, 12 | sep; 13 | 14 | /** 15 | * @constructor 16 | * */ 17 | function Mediator (separator) { 18 | /** 19 | * По умолчанию неймспейсы будут разделены двоеточием 20 | * */ 21 | sep = separator || ':'; 22 | } 23 | 24 | /** 25 | * Добавляет подписчик в медиатор 26 | * @this {Mediator} 27 | * @param {string} channel Канал, на который подписывается обработчик 28 | * @param {function} handler Обработчик 29 | * @param {object} context Контекст выполнения обработчика 30 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов 31 | * */ 32 | Mediator.prototype.on = function (channel, handler, context) { 33 | if (channel.length) { 34 | this.namespace(channel).handlers.push({ 35 | fn: handler, 36 | context: context 37 | }); 38 | } 39 | 40 | return this; 41 | }; 42 | 43 | /** 44 | * Удаляет подписчики из медиатора 45 | * @this {Mediator} 46 | * @param {string} channel Канал, обработчики которого будут удалены 47 | * @param {boolean} withNested Логическое поле, которое позволяет отключить обработчики каналов-потомков 48 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов 49 | */ 50 | Mediator.prototype.off = function(channel, withNested) { 51 | if (this.has(channel)) { 52 | var ch = this.namespace(channel); 53 | ch.handlers = []; 54 | 55 | if (withNested) { 56 | ch.nested = {}; 57 | } 58 | } 59 | 60 | return this; 61 | }; 62 | 63 | /** 64 | * Вызывает обработчики для конкретного канала с передачей в них данных 65 | * @private 66 | * @this {Mediator} 67 | * @param {array} handlers Массив с обработчиками 68 | * @param {object} data Объект с данными, который будет передан в качестве аргумента в обработчики 69 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов 70 | */ 71 | Mediator.prototype._runHandlers = function(handlers, data) { 72 | for (var length = handlers.length, i = 0; i < length; i++) { 73 | handlers[i].fn.call(handlers[i].context, data); 74 | } 75 | 76 | return this; 77 | }; 78 | 79 | /** 80 | * Рекурсивно вызывает обработчики с передачей в них данных 81 | * @private 82 | * @this {Mediator} 83 | * @param {object} namespace Пространство имен 84 | * @param {object} data Объект с данными, который будет передан в качестве аргумента в обработчики 85 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов 86 | */ 87 | Mediator.prototype._runHandlersRecursive = function(namespace, data) { 88 | this._runHandlers(namespace.handlers, data); 89 | 90 | for (var ns in namespace.nested) { 91 | if (namespace.nested.hasOwnProperty(ns)) { 92 | this._runHandlersRecursive(namespace.nested[ns], data); 93 | } 94 | } 95 | 96 | return this; 97 | } 98 | 99 | /** 100 | * Вызывает обработчики для конкретного канала с передачей в них данных 101 | * @this {Mediator} 102 | * @param {string} channel Имя канала 103 | * @param {object} data Объект с данными, который будет передан в качестве аргумента в обработчики 104 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов 105 | */ 106 | Mediator.prototype.trigger = function(channel, data) { 107 | if (this.has(channel)) { 108 | this._runHandlers(this.namespace(channel).handlers, data); 109 | } else { 110 | throw new Error('The channel does not exist!'); 111 | } 112 | 113 | return this; 114 | }; 115 | 116 | /** 117 | * Вызывает обработчики для конкретного канала и вложенных каналов с передачей в них данных 118 | * @this {Mediator} 119 | * @param {string} channel Имя канала 120 | * @param {object} data Объект с данными, который будет передан в качестве аргумента в обработчики 121 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов 122 | */ 123 | Mediator.prototype.broadcast = function (channel, data) { 124 | if (this.has(channel)) { 125 | this._runHandlersRecursive(this.namespace(channel), data); 126 | } else { 127 | throw new Error('The channel does not exist!'); 128 | } 129 | 130 | return this; 131 | }; 132 | 133 | /** 134 | * Удаление все обработчиков из медиатора 135 | * @this {Mediator} 136 | * @returns {object} Ссылка на объект-медиатор для цепочки вызовов 137 | */ 138 | Mediator.prototype.clean = function (){ 139 | storage = {}; 140 | return this; 141 | }; 142 | 143 | /** 144 | * Проверка существования определенного неймспейса 145 | * @this {Mediator} 146 | * @param {string} channel Канал, который будет проверяться на наличие 147 | * @returns {boolean} Истина/ложь 148 | * */ 149 | Mediator.prototype.has = function (channel) { 150 | if (!channel.length) return false; 151 | var parts = channel.split(sep), 152 | length = parts.length, 153 | i = 0, 154 | current = storage; 155 | 156 | for (; i < length; i++) { 157 | if (current[parts[i]] !== undefined) { 158 | current = current[parts[i]].nested; 159 | } else { 160 | return false; 161 | } 162 | } 163 | 164 | return true; 165 | }; 166 | 167 | /** 168 | * Функция для управления пространством имен обработчиков 169 | * @this {Mediator} 170 | * @param {string} channel Канал 171 | * @returns {object} current Хранилище обработчиков канала 172 | * */ 173 | Mediator.prototype.namespace = function (channel) { 174 | var parts = channel.split(sep), 175 | length = parts.length, 176 | i = 0, 177 | current = storage; 178 | 179 | /** 180 | * Возвращает объект заданной структуры 181 | * 182 | * @returns {object} 183 | * */ 184 | function getUnit() { 185 | return { 186 | nested: {}, 187 | handlers: [] 188 | } 189 | } 190 | 191 | for (; i < length; i++) { 192 | current[parts[i]] = current[parts[i]] || getUnit(); 193 | current = (i !== length - 1 ? current[parts[i]].nested : current[parts[i]]); 194 | } 195 | 196 | return current; 197 | } 198 | 199 | /** 200 | * Возвращает содержимое объекта с обработчиками. Только для отладки. 201 | * @this {Mediator} 202 | * @returns {object} storage Хранилище обработчиков 203 | * */ 204 | Mediator.prototype.getStorage = function() { 205 | return storage; 206 | }; 207 | 208 | return Mediator; 209 | })(); 210 | -------------------------------------------------------------------------------- /patterns/behavioral/mediator/spec/mediator.spec.js: -------------------------------------------------------------------------------- 1 | describe('Посредник / Mediator', function () { 2 | var mediator; 3 | 4 | describe('Проверка объекта', function () { 5 | 6 | before(function () { 7 | mediator = new Mediator(); 8 | }); 9 | 10 | it('Инстанс', function () { 11 | expect(mediator).to.be.instanceOf(Mediator); 12 | }); 13 | 14 | it('Методы', function() { 15 | expect(mediator.on).to.be.a('function'); 16 | expect(mediator.off).to.be.a('function'); 17 | expect(mediator.trigger).to.be.a('function'); 18 | expect(mediator.broadcast).to.be.a('function'); 19 | expect(mediator.has).to.be.a('function'); 20 | expect(mediator.clean).to.be.a('function'); 21 | expect(mediator.namespace).to.be.a('function'); 22 | expect(mediator.getStorage).to.be.a('function'); 23 | expect(mediator._runHandlers).to.be.a('function'); 24 | expect(mediator._runHandlersRecursive).to.be.a('function'); 25 | }); 26 | }); 27 | 28 | describe('Поведение', function () { 29 | 30 | var aFn, bFn, cFn, dFn, eFn; 31 | 32 | beforeEach(function () { 33 | mediator = new Mediator(); 34 | 35 | aFn = sinon.spy(); 36 | bFn = sinon.spy(); 37 | cFn = sinon.spy(); 38 | dFn = sinon.spy(); 39 | eFn = sinon.spy(); 40 | }); 41 | 42 | it('Добавление обработчиков', function () { 43 | mediator 44 | .on('namespace', aFn) 45 | .on('namespace', bFn) 46 | .on('namespace', cFn) 47 | .on('namespace', dFn) 48 | .trigger('namespace'); 49 | 50 | 51 | expect(aFn.called).to.be.true; 52 | expect(bFn.called).to.be.true; 53 | expect(cFn.called).to.be.true; 54 | expect(dFn.called).to.be.true; 55 | }); 56 | 57 | it('Добавление обработчика с данными', function () { 58 | 59 | var o = { text: 'Hello' }; 60 | 61 | mediator 62 | .on('namespace', aFn) 63 | .on('namespace', bFn) 64 | .on('namespace', cFn) 65 | .on('namespace', dFn) 66 | .trigger('namespace', o); 67 | 68 | 69 | expect(aFn.calledWith(o)).to.be.true; 70 | expect(bFn.calledWith(o)).to.be.true; 71 | expect(cFn.calledWith(o)).to.be.true; 72 | expect(dFn.calledWith(o)).to.be.true; 73 | }); 74 | 75 | it('Добавление вложенных обработчиков', function () { 76 | mediator 77 | .on('a', aFn) 78 | .on('a:b', bFn) 79 | .on('a:b:c', cFn) 80 | .on('a:b:c:d', dFn) 81 | .trigger('a') 82 | .trigger('a:b:c'); 83 | 84 | 85 | expect(aFn.called).to.be.true; 86 | expect(bFn.called).to.be.false; 87 | expect(cFn.called).to.be.true; 88 | expect(dFn.called).to.be.false; 89 | }); 90 | 91 | it('Запуск всех вложенных обработчиков', function () { 92 | var o = { text: 'Hello' }; 93 | 94 | mediator 95 | .on('a', aFn) 96 | .on('a:b', bFn) 97 | .on('a:b:c', cFn) 98 | .on('a:b:c:d', dFn) 99 | .broadcast('a', o); 100 | 101 | expect(aFn.calledWith(o)).to.be.true; 102 | expect(bFn.calledWith(o)).to.be.true; 103 | expect(cFn.calledWith(o)).to.be.true; 104 | expect(dFn.calledWith(o)).to.be.true; 105 | }); 106 | 107 | it('Удаление из списка обработчиков', function () { 108 | 109 | mediator 110 | .on('namespace', aFn) 111 | .on('namespace', bFn) 112 | .off('namespace') 113 | .on('namespace', cFn) 114 | .trigger('namespace'); 115 | 116 | expect(aFn.called).to.be.false; 117 | expect(bFn.called).to.be.false; 118 | expect(cFn.called).to.be.true; 119 | }); 120 | 121 | it('Удаление всех вложенных обработчиков', function () { 122 | mediator 123 | .on('a', aFn) 124 | .on('a:b', bFn) 125 | .on('a:b:c', cFn) 126 | .on('a:b:c:d', dFn) 127 | .off('a', true); 128 | 129 | expect(aFn.called).to.be.false; 130 | expect(bFn.called).to.be.false; 131 | expect(cFn.called).to.be.false; 132 | expect(dFn.called).to.be.false; 133 | }); 134 | }); 135 | }); 136 | 137 | 138 | 139 | 140 | ///** 141 | // * Пример использования 142 | // * */ 143 | //var m = new Mediator(); 144 | // 145 | ///** 146 | // * Добавляем обработчик 147 | // * */ 148 | //m.on('hello', function (data) { 149 | // console.log('Эти данные пришли из медиатора: hello -> ' + data.name); 150 | //}); 151 | // 152 | //m.on('hello:world', function(data) { 153 | // console.log('Эти данные пришли из медиатора: hello:world -> ' + data.name); 154 | //}); 155 | // 156 | //m.on('hello:world:a', function(data) { 157 | // console.log('Эти данные пришли из медиатора: hello:world:a -> ' + data.name); 158 | //}); 159 | // 160 | //m.on('hello:world:b', function(data) { 161 | // console.log('Эти данные пришли из медиатора: hello:world:b -> ' + data.name); 162 | //}); 163 | // 164 | //m.on('hello:world:c', function(data) { 165 | // console.log('Эти данные пришли из медиатора: hello:world:c -> ' + data.name); 166 | //}); 167 | // 168 | //m.on('hello:world:c:d', function(data) { 169 | // console.log('Эти данные пришли из медиатора: hello:world:c:d -> ' + data.name); 170 | //}); 171 | // 172 | //m.on('hello:another', function(data) { 173 | // console.log('Эти данные пришли из медиатора: hello:another -> ' + data.name); 174 | //}); 175 | // 176 | //m.on('hello:another:world', function(data) { 177 | // console.log('Эти данные пришли из медиатора: hello:another:world -> ' + data.name); 178 | //}); 179 | // 180 | //m.on('hello:another:world:a', function(data) { 181 | // console.log('Эти данные пришли из медиатора: hello:another:world:a -> ' + data.name); 182 | //}); 183 | // 184 | //m.on('hello:another:world:b', function(data) { 185 | // console.log('Эти данные пришли из медиатора: hello:another:world:b -> ' + data.name); 186 | //}); 187 | // 188 | ////m.off('hello', true); 189 | // 190 | //m.on('goodbye', function(data) { 191 | // console.log('Эти данные пришли из медиатора: goodbye -> ' + data.name); 192 | //}); 193 | // 194 | //m.on('goodbye:world', function(data) { 195 | // console.log('Эти данные пришли из медиатора: goodbye:world ->' + data.name); 196 | //}); 197 | // 198 | //m.on('goodbye:another:world', function(data) { 199 | // console.log('Эти данные пришли из медиатора: goodbye:another:world -> ' + data.name); 200 | //}); 201 | // 202 | //m.on('lorem:ipsum:dolor:sit:amet', function(data) { 203 | // console.log('Эти данные пришли из медиатора: lorem:ipsum:dolor:sit:amet -> ' + data.name); 204 | //}); 205 | // 206 | //m.on('lorem:lorem', function(data) { 207 | // console.log('Эти данные пришли из медиатора: lorem:lorem -> ' + data.name); 208 | //}); 209 | // 210 | //m.on('lorem:ipsum', function(data) { 211 | // console.log('Эти данные пришли из медиатора: lorem:ipsum -> ' + data.name); 212 | //}); 213 | // 214 | ////m.off('lorem:ipsum:dolor'); 215 | // 216 | ///** 217 | // * Вызываем обработчик 218 | // * */ 219 | //m.broadcast('hello', { name: 'Аполлинарий Лаодикийский' }); 220 | //m.trigger('hello:world', { name: 'Вильгельм фон Каульбах' }); 221 | ////m.trigger('', { name: 'Дени Дидро' }); 222 | ////m.trigger('', { name: 'Фридрих Ницше' }); 223 | ////m.trigger('', { name: 'Иммануил Кант' }); 224 | ////m.trigger('', { name: 'Анри Пуанкаре' }); 225 | ////m.trigger('', { name: 'Эварист Галуа' }); 226 | //m.trigger('hello:another:world', { name: 'Якоб Бернулли' }); 227 | //m.trigger('goodbye', { name: 'Жан Даламбер' }); 228 | //m.trigger('goodbye:world', { name: 'Алан Тьюринг' }); 229 | //m.trigger('goodbye:another:world', { name: 'Норберт Винер' }); 230 | // 231 | // 232 | //m.trigger('lorem:ipsum:dolor:sit:amet', { name: 'Александр Ляпунов' }); 233 | //m.trigger('lorem:ipsum', { name: 'Артур Шопенгауэр' }); 234 | //m.trigger('lorem:lorem', { name: 'Карл Вейерштрасс' }); 235 | //m.broadcast('lorem', { name: 'Рекурсивно' }); 236 | // 237 | ////console.log(m.getStorage()); -------------------------------------------------------------------------------- /assets/underscore.min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.6.0 2 | // http://underscorejs.org 3 | // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 4 | // Underscore may be freely distributed under the MIT license. 5 | (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?void(this._wrapped=n):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.6.0";var A=j.each=j.forEach=function(n,t,e){if(null==n)return n;if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return;return n};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var O="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},j.find=j.detect=function(n,t,r){var e;return k(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var k=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:k(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,j.property(t))},j.where=function(n,t){return j.filter(n,j.matches(t))},j.findWhere=function(n,t){return j.find(n,j.matches(t))},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);var e=-1/0,u=-1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;o>u&&(e=n,u=o)}),e},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);var e=1/0,u=1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;u>o&&(e=n,u=o)}),e},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=j.values(n)),n[j.random(n.length-1)]):j.shuffle(n).slice(0,Math.max(0,t))};var E=function(n){return null==n?j.identity:j.isFunction(n)?n:j.property(n)};j.sortBy=function(n,t,r){return t=E(t),j.pluck(j.map(n,function(n,e,u){return{value:n,index:e,criteria:t.call(r,n,e,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=E(r),A(t,function(i,a){var o=r.call(e,i,a,t);n(u,o,i)}),u}};j.groupBy=F(function(n,t,r){j.has(n,t)?n[t].push(r):n[t]=[r]}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=E(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])t?[]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.partition=function(n,t){var r=[],e=[];return A(n,function(n){(t(n)?r:e).push(n)}),[r,e]},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.contains(t,n)})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===j&&(e[u]=arguments[r++]);for(;r=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u),e=u=null):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o,c=function(){var l=j.now()-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u),i=u=null))};return function(){i=this,u=arguments,a=j.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u),i=u=null),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return j.partial(t,n)},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=function(n){if(!j.isObject(n))return[];if(w)return w(n);var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o)&&"constructor"in n&&"constructor"in t)return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.constant=function(n){return function(){return n}},j.property=function(n){return function(t){return t[n]}},j.matches=function(n){return function(t){if(t===n)return!0;for(var r in n)if(n[r]!==t[r])return!1;return!0}},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},j.now=Date.now||function(){return(new Date).getTime()};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};T.unescape=j.invert(T.escape);var I={escape:new RegExp("["+j.keys(T.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(T.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(I[n],function(t){return T[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),"function"==typeof define&&define.amd&&define("underscore",[],function(){return j})}).call(this); 6 | //# sourceMappingURL=underscore-min.map --------------------------------------------------------------------------------