├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── img ├── books.gif └── logo.png ├── index.html ├── js ├── adapter │ ├── init.js │ ├── newInterface.js │ ├── oldInterface.js │ └── oldInterfaceAdapter.js ├── composite │ ├── init.js │ └── node.js ├── cor │ ├── handler.js │ ├── handlers │ │ ├── call.js │ │ ├── email.js │ │ └── sms.js │ └── init.js ├── decorator │ ├── decorators │ │ ├── admin.js │ │ └── exec.js │ ├── init.js │ └── user.js ├── facade │ ├── facade.js │ ├── init.js │ └── processor.js ├── factory │ ├── image.js │ ├── init.js │ ├── mediaFactory.js │ └── video.js ├── iterator │ ├── init.js │ └── iterator.js ├── main.js ├── mediator │ ├── colleague.js │ ├── init.js │ └── mediator.js ├── observer │ ├── init.js │ ├── observer.js │ ├── observers.js │ └── subject.js ├── proxy │ ├── init.js │ ├── slowObject.js │ └── slowObjectProxy.js ├── pubsub │ ├── init.js │ ├── moduleA.js │ ├── moduleB.js │ └── pubsub.js └── strategy │ ├── emailValidator.js │ ├── init.js │ ├── strategy.js │ └── telValidator.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Tuts+ 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Put JavaScript Design Patterns Into Practice][published url] 2 | ## Instructor: [Dan Wellman][instructor url] 3 | 4 | 5 | A design pattern is a proven solution to a common software problem. Patterns codify good design principles and facilitate maintainable code that can be worked on by teams. 6 | Design patterns are also a way for teams to communicate about the structure and architecture of an application. Patterns are a language that can be used to convey ideas and facilitate discussions. 7 | 8 | In this course, Tuts+ instructor Dan Wellman will cover some of the most common and most useful design patterns used in JavaScript. Each pattern will be demonstrated with a standalone example bundled in a JavaScript module. 9 | 10 | ## Source Files Description 11 | 12 | This repository contains an example of each of the design patterns discussed in the course. 13 | 14 | 1. Clone repository to your machine 15 | `git clone https://github.com/danwellman/js-patterns.git` 16 | 17 | 2. Run Bower install 18 | `bower install` 19 | 20 | 3. Open index.html in your browser and open the browser's console 21 | 22 | 4. Use the global runExample() method to run one of the examples 23 | `runExample('factory')` 24 | 25 | Valid example names (to be passed as a string to the runExample() function) are: 26 | 27 | * `factory` 28 | * `pubsub` 29 | * `strategy` 30 | * `observer` 31 | * `cor` 32 | * `mediator` 33 | * `iterator` 34 | * `facade` 35 | * `decorator` 36 | * `adapter` 37 | * `composite` 38 | * `proxy` 39 | 40 | ------ 41 | 42 | These are source files for the Tuts+ course: [Put JavaScript Design Patterns Into Practice][published url] 43 | 44 | Available on [Tuts+](https://tutsplus.com). Teaching skills to millions worldwide. 45 | 46 | [published url]: https://code.tutsplus.com/courses/put-javascript-design-patterns-into-practice 47 | [instructor url]: https://tutsplus.com/authors/dan-wellman 48 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jspatterns", 3 | "version": "1.0.0", 4 | "authors": [ 5 | "Dan " 6 | ], 7 | "moduleType": [ 8 | "amd" 9 | ], 10 | "license": "MIT", 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "test", 16 | "tests" 17 | ], 18 | "devDependencies": { 19 | "requirejs": "~2.1.20" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /img/books.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tutsplus/put-javascript-design-patterns-into-practice/9b7474dbe20a796427ffca0190868b3d65a3fef9/img/books.gif -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tutsplus/put-javascript-design-patterns-into-practice/9b7474dbe20a796427ffca0190868b3d65a3fef9/img/logo.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Put JavaScript Design Patterns into Practice 7 | 43 | 44 | 45 |
46 | 47 |
48 |

Put JavaScript Design Patterns into Practice

49 |

by Dan Wellman

50 |
51 |
52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /js/adapter/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var oldInterfaceAdapter = require('adapter/oldInterfaceAdapter'); 8 | 9 | oldInterfaceAdapter.doSomethingOld(); 10 | } 11 | 12 | }; 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /js/adapter/newInterface.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var NewInterface = function () { }; 5 | 6 | NewInterface.prototype.doSomethingNew = function (newArg) { 7 | console.log('doing the ', newArg); 8 | }; 9 | 10 | return new NewInterface(); 11 | }); 12 | -------------------------------------------------------------------------------- /js/adapter/oldInterface.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var OldInterface = function () { }; 5 | 6 | OldInterface.prototype.doSomethingOld = function () { 7 | console.log('doing the old thing'); 8 | }; 9 | 10 | return new OldInterface(); 11 | }); 12 | -------------------------------------------------------------------------------- /js/adapter/oldInterfaceAdapter.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var newInterface = require('adapter/newInterface'); 5 | 6 | return { 7 | doSomethingOld: function () { 8 | return newInterface.doSomethingNew('new thing'); 9 | } 10 | }; 11 | }); 12 | -------------------------------------------------------------------------------- /js/composite/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var Node = require('composite/node'), 8 | root = new Node('Fred'), 9 | child1 = new Node('John'), 10 | child2 = new Node('Jane'), 11 | child3 = new Node('Jack'), 12 | child4 = new Node('Jill'), 13 | child5 = new Node('James'), 14 | child6 = new Node('Jess'); 15 | 16 | root.addChild(child1); 17 | root.addChild(child2); 18 | 19 | child2.addChild(child3); 20 | child2.addChild(child4); 21 | 22 | child4.addChild(child5); 23 | 24 | child5.addChild(child6); 25 | 26 | //root.traverseDown(); 27 | //child6.traverseDown(); 28 | 29 | //child6.traverseUp(); 30 | root.traverseUp(); 31 | } 32 | 33 | }; 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /js/composite/node.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Person = function (name) { 5 | this.name = name; 6 | this.children = []; 7 | this.parent = null; 8 | }; 9 | 10 | Person.prototype.addChild = function (child) { 11 | this.children.push(child); 12 | child.parent = this; 13 | }; 14 | 15 | Person.prototype.traverseUp = function () { 16 | if (this.parent) { 17 | console.log(this.name + ' is the child of ' + this.parent.name); 18 | this.parent.traverseUp(); 19 | } else { 20 | console.log(this.name + ' is the root node'); 21 | } 22 | }; 23 | 24 | Person.prototype.traverseDown = function () { 25 | if (this.children.length) { 26 | this.children.forEach(function (child) { 27 | console.log(this.name + ' is the parent of ' + child.name); 28 | child.traverseDown(); 29 | }, this); 30 | } else { 31 | console.log(this.name + ' is a leaf node'); 32 | } 33 | }; 34 | 35 | return Person; 36 | }); 37 | -------------------------------------------------------------------------------- /js/cor/handler.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var CommunicationHandler = function (communicationType, handler, nextHandler) { 5 | this.communicationType = communicationType; 6 | this.handler = handler; 7 | this.nextHandler = nextHandler; 8 | }; 9 | 10 | CommunicationHandler.prototype.handleCommunication = function (communication) { 11 | if (communication.type !== this.communicationType) { 12 | (this.nextHandler) ? this.nextHandler.handleCommunication(communication) : 13 | console.log('Communication type', communication.type, 'could not be handled'); 14 | return; 15 | } 16 | this.handler(communication); 17 | }; 18 | 19 | return CommunicationHandler; 20 | }); 21 | -------------------------------------------------------------------------------- /js/cor/handlers/call.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var Handler = require('cor/handler'), 5 | smsHandler = require('cor/handlers/sms'), 6 | callHandler; 7 | 8 | callHandler = new Handler('call', handleCall, smsHandler); 9 | 10 | function handleCall(call) { 11 | console.log('Call placed to number', call.number, 'from number', call.ownNumber); 12 | } 13 | 14 | return callHandler; 15 | }); 16 | -------------------------------------------------------------------------------- /js/cor/handlers/email.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var Handler = require('cor/handler'), 5 | emailHandler; 6 | 7 | emailHandler = new Handler('email', handleEmail, null); 8 | 9 | function handleEmail(email) { 10 | console.log('Email sent to', email.recipient, 'message: ', email.message); 11 | } 12 | 13 | return emailHandler; 14 | }); 15 | -------------------------------------------------------------------------------- /js/cor/handlers/sms.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var Handler = require('cor/handler'), 5 | emailHandler = require('cor/handlers/email'), 6 | smsHandler; 7 | 8 | smsHandler = new Handler('sms', handleSms, emailHandler); 9 | 10 | function handleSms(sms) { 11 | console.log('SMS sent to number', sms.number, 'message: ', sms.message); 12 | } 13 | 14 | return smsHandler; 15 | }); 16 | -------------------------------------------------------------------------------- /js/cor/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var call, sms, email, handler, telepathy, 8 | Handler = require('cor/handler'), 9 | callHandler = require('cor/handlers/call'); 10 | 11 | call = { 12 | type: 'call', 13 | number: '07123456789', 14 | ownNumber: '070031101003' 15 | }; 16 | 17 | sms = { 18 | type: 'sms', 19 | number: '07123456789', 20 | message: 'Hey Dan' 21 | }; 22 | 23 | email = { 24 | type: 'email', 25 | recipient: 'dan@danwellman.co.uk', 26 | message: 'Hi Dan' 27 | }; 28 | 29 | telepathy = { 30 | type: 'esp', 31 | target: 'someone else', 32 | message: 'spooky' 33 | }; 34 | 35 | handler = new Handler(null, null, callHandler); 36 | handler.handleCommunication(email); 37 | handler.handleCommunication(sms); 38 | handler.handleCommunication(call); 39 | handler.handleCommunication(telepathy); 40 | 41 | } 42 | 43 | }; 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /js/decorator/decorators/admin.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | return { 5 | getPermissions: function () { 6 | return 'public:write,confidential:write' 7 | } 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /js/decorator/decorators/exec.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | return { 5 | getPermissions: function () { 6 | return 'public:read,confidential:read' 7 | } 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /js/decorator/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var user1, user2, 8 | User = require('decorator/user'), 9 | execDecorator = require('decorator/decorators/exec'), 10 | adminDecorator = require('decorator/decorators/admin'); 11 | 12 | user1 = new User('user1'); 13 | user1.decoratePermissions(execDecorator); 14 | 15 | user2 = new User('user2'); 16 | user2.decoratePermissions(adminDecorator); 17 | 18 | console.log(user1.id, user1.getPermissions()); 19 | console.log(user2.id, user2.getPermissions()); 20 | } 21 | 22 | }; 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /js/decorator/user.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var User = function (id) { 5 | this.id = id; 6 | this.getPermissions = function () { 7 | return 'public:read'; 8 | } 9 | }; 10 | 11 | User.prototype.decoratePermissions = function (decorator) { 12 | this.getPermissions = decorator.getPermissions; 13 | }; 14 | 15 | return User; 16 | }); 17 | -------------------------------------------------------------------------------- /js/facade/facade.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var processor = require('facade/processor'); 5 | 6 | return { 7 | processThing: function (thing) { 8 | switch (Object.prototype.toString.call(thing)) { 9 | case '[object String]': 10 | return processor.processString(thing); 11 | break; 12 | case '[object Number]': 13 | return processor.processNumber(thing); 14 | break; 15 | case '[object Boolean]': 16 | return processor.processBoolean(thing); 17 | break; 18 | case '[object Array]': 19 | return processor.processArray(thing); 20 | break; 21 | case '[object Object]': 22 | return processor.processObject(thing); 23 | break; 24 | default: 25 | return 'Unable to process the thing!'; 26 | } 27 | } 28 | }; 29 | }); 30 | -------------------------------------------------------------------------------- /js/facade/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var facade = require('facade/facade'); 8 | 9 | console.log(facade.processThing('test string')); 10 | console.log(facade.processThing(5)); 11 | console.log(facade.processThing(true)); 12 | console.log(facade.processThing([1, 2, 3])); 13 | console.log(facade.processThing({ prop: 'something' })); 14 | } 15 | 16 | }; 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /js/facade/processor.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | return { 5 | processString: function (string) { 6 | return string.substring(0, string.length / 2); 7 | }, 8 | processNumber: function (number) { 9 | return number * number; 10 | }, 11 | processBoolean: function (bool) { 12 | return !bool; 13 | }, 14 | processArray: function (array) { 15 | return array.length; 16 | }, 17 | processObject: function (object) { 18 | return Object.keys(object).length; 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /js/factory/image.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Image = function (attributes) { 5 | this.width = attributes.width || 0; 6 | this.height = attributes.height || 0; 7 | this.name = attributes.name || ''; 8 | }; 9 | 10 | return Image; 11 | }); 12 | -------------------------------------------------------------------------------- /js/factory/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var myVideo, myImage, 8 | mediaFactory = require('factory/mediaFactory'); 9 | 10 | myVideo = mediaFactory.createMedia('Video', { 11 | length: 3.5, 12 | name: 'My video' 13 | }); 14 | 15 | myImage = mediaFactory.createMedia('Image', { 16 | width: 100, 17 | height: 100, 18 | name: 'My image' 19 | }); 20 | 21 | console.log(myVideo); 22 | console.log(myImage); 23 | } 24 | 25 | }; 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /js/factory/mediaFactory.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var media = { 5 | Video: require('factory/video'), 6 | Image: require('factory/image') 7 | }; 8 | 9 | return { 10 | createMedia: function (type, attributes) { 11 | var MediaType = media[type]; 12 | 13 | return new MediaType(attributes); 14 | } 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /js/factory/video.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Video = function (attributes) { 5 | this.length = attributes.length || 0; 6 | this.name = attributes.name || ''; 7 | }; 8 | 9 | return Video; 10 | }); 11 | -------------------------------------------------------------------------------- /js/iterator/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var iterator = require('iterator/iterator'), 8 | testArray = [{ something: 'yay', other: 123 }, { something: 'test', other: 456 }], 9 | myArrayIterator = iterator.build(testArray), 10 | testString = 'teststring', 11 | myStringIterator = iterator.build(testString); 12 | 13 | console.log(myArrayIterator.next()); 14 | console.log(myArrayIterator.next()); 15 | 16 | while (!myStringIterator.isDone()) { 17 | console.log(myStringIterator.next()); 18 | } 19 | 20 | console.log(myStringIterator.reset().take(4).join('')); 21 | } 22 | 23 | }; 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /js/iterator/iterator.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Iterator = function (collection) { 5 | this.collection = collection; 6 | this.index = 0; 7 | }; 8 | 9 | Iterator.prototype = { 10 | constructor: Iterator, 11 | next: function () { 12 | return this.collection[this.index++]; 13 | }, 14 | isDone: function () { 15 | return this.index === this.collection.length; 16 | }, 17 | reset: function () { 18 | this.index = 0; 19 | return this; 20 | }, 21 | take: function (numberOfItems) { 22 | var newIndex = this.index + numberOfItems, 23 | newArr = Array.prototype.slice.call(this.collection, this.index, newIndex); 24 | 25 | this.index = newIndex; 26 | return newArr; 27 | } 28 | }; 29 | 30 | return { 31 | build: function (collection) { 32 | var keys = Object.keys(collection), 33 | tempArray = [], 34 | prop; 35 | 36 | if (typeof collection === 'number') { 37 | collection = collection.toString(); 38 | } 39 | 40 | if (keys.length) { 41 | for (prop in collection) { 42 | tempArray.push(collection[prop]); 43 | } 44 | collection = tempArray; 45 | } 46 | 47 | if (collection.length) { 48 | return new Iterator(collection); 49 | } else { 50 | throw ('Iterator cannot be built from Boolean, null or undefined'); 51 | } 52 | } 53 | } 54 | }); 55 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | require( 2 | ['factory/init', 'pubsub/init', 'strategy/init', 'observer/init', 'cor/init', 'mediator/init', 'iterator/init', 3 | 'facade/init', 'decorator/init', 'adapter/init', 'composite/init', 'proxy/init'], 4 | function (factory, pubsub, strategy, observer, cor, mediator, iterator, facade, decorator, adapter, composite, proxy) { 5 | 'use strict'; 6 | 7 | var examples = { 8 | factory: factory, 9 | pubsub: pubsub, 10 | strategy: strategy, 11 | observer: observer, 12 | cor: cor, 13 | mediator: mediator, 14 | iterator: iterator, 15 | facade: facade, 16 | decorator: decorator, 17 | adapter: adapter, 18 | composite: composite, 19 | proxy: proxy 20 | }; 21 | 22 | window.runExample = function (example) { 23 | examples[example].init(); 24 | }; 25 | } 26 | ); 27 | -------------------------------------------------------------------------------- /js/mediator/colleague.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Colleague = function (id, mediator) { 5 | this.id = id; 6 | this.mediator = mediator; 7 | }; 8 | 9 | Colleague.prototype.receiveMessage = function (message) { 10 | console.log('Module', this.id, 'received message:', message); 11 | return true; 12 | }; 13 | 14 | Colleague.prototype.sendMessage = function (message, recipientId) { 15 | (recipientId) ? this.mediator.send(recipientId, message) : 16 | this.mediator.broadcast(message, this); 17 | }; 18 | 19 | return { 20 | create: function (id, mediator) { 21 | var that = new Colleague(id, mediator); 22 | 23 | mediator.register(that); 24 | 25 | return that; 26 | } 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /js/mediator/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var mediator, colleague1, colleague2, colleague3, 8 | Mediator = require('mediator/mediator'), 9 | colleague = require('mediator/colleague'); 10 | 11 | mediator = new Mediator(); 12 | colleague1 = colleague.create('colleague1', mediator); 13 | colleague2 = colleague.create('colleague2', mediator); 14 | colleague3 = colleague.create('colleague3', mediator); 15 | 16 | colleague1.sendMessage('Hey there', 'colleague2'); 17 | colleague2.sendMessage('Hi colleague1', 'colleague1'); 18 | colleague3.sendMessage('Hey guys!'); 19 | } 20 | 21 | }; 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /js/mediator/mediator.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Mediator = function () { 5 | this.colleagues = []; 6 | }; 7 | 8 | Mediator.prototype.register = function (colleague) { 9 | this.colleagues.push(colleague); 10 | }; 11 | 12 | Mediator.prototype.send = function (recipientId, message) { 13 | this.colleagues.some(function (colleague) { 14 | if (colleague.id === recipientId) { 15 | return colleague.receiveMessage(message); 16 | } 17 | }); 18 | }; 19 | 20 | Mediator.prototype.broadcast = function (message, sender) { 21 | this.colleagues.forEach(function (colleague) { 22 | if (colleague.id !== sender.id) { 23 | colleague.receiveMessage(message); 24 | } 25 | }); 26 | }; 27 | 28 | return Mediator; 29 | }); 30 | -------------------------------------------------------------------------------- /js/observer/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var subject, observer, otherObserver, data, moreData, 8 | Subject = require('observer/subject'), 9 | Observer = require('observer/observer'); 10 | 11 | subject = new Subject(); 12 | observer = new Observer('observer1'); 13 | otherObserver = new Observer('observer2'); 14 | 15 | data = { 16 | prop: 'something' 17 | }; 18 | moreData = { 19 | prop: 'something else' 20 | }; 21 | 22 | subject.observe(observer); 23 | subject.observe(otherObserver); 24 | 25 | subject.add(data); 26 | subject.add(moreData); 27 | 28 | subject.unObserve(observer); 29 | 30 | subject.remove(data); 31 | } 32 | 33 | }; 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /js/observer/observer.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Observer = function (name) { 5 | this.name = name; 6 | }; 7 | 8 | Observer.prototype.notify = function (event, data) { 9 | console.log('The event was ', '"' + event + '",', 'the data was', data, 'and I am ', this.name); 10 | }; 11 | 12 | return Observer; 13 | }); 14 | -------------------------------------------------------------------------------- /js/observer/observers.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Observers = function () { 5 | this.observers = []; 6 | }; 7 | 8 | Observers.prototype.add = function (observer) { 9 | this.observers.push(observer); 10 | }; 11 | 12 | Observers.prototype.remove = function (observerToRemove) { 13 | this.observers = this.observers.filter(function (observer) { 14 | return observer !== observerToRemove; 15 | }); 16 | }; 17 | 18 | Observers.prototype.get = function () { 19 | return this.observers; 20 | }; 21 | 22 | return Observers; 23 | }); 24 | -------------------------------------------------------------------------------- /js/observer/subject.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var Observers = require('observer/observers'); 5 | 6 | var Collection = function (items) { 7 | this.observers = new Observers(); 8 | this.collection = items || []; 9 | }; 10 | 11 | Collection.prototype.observe = function (observer) { 12 | this.observers.add(observer); 13 | }; 14 | 15 | Collection.prototype.unObserve = function (observer) { 16 | this.observers.remove(observer); 17 | }; 18 | 19 | Collection.prototype.notify = function (event, data) { 20 | this.observers.get().forEach(function (observer) { 21 | observer.notify(event, data); 22 | }); 23 | }; 24 | 25 | Collection.prototype.add = function (item) { 26 | this.collection.push(item); 27 | this.notify('added', item); 28 | }; 29 | 30 | Collection.prototype.remove = function (itemToRemove) { 31 | this.collection = this.collection.filter(function (item) { 32 | if (item !== itemToRemove) { 33 | return true; 34 | } 35 | this.notify('removed', item); 36 | return false; 37 | }, this); 38 | }; 39 | 40 | return Collection; 41 | }); 42 | -------------------------------------------------------------------------------- /js/proxy/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var myProxy, 8 | slowObjectProxy = require('proxy/slowObjectProxy'); 9 | 10 | myProxy = slowObjectProxy.init(); 11 | myProxy.someMethod(); 12 | } 13 | 14 | }; 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /js/proxy/slowObject.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var SlowObject = function () { 5 | this.someMethod = function () { 6 | console.log('some method on the slow object was invoked'); 7 | } 8 | }; 9 | 10 | return { 11 | init: function () { 12 | for (var x = 0, max = 1000; x < max; x++) { 13 | console.log('slowness...'); 14 | } 15 | return new SlowObject(); 16 | } 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /js/proxy/slowObjectProxy.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var SlowObjectProxy, slowObjectInstance, 5 | slowObject = require('proxy/slowObject'); 6 | 7 | SlowObjectProxy = function () { 8 | this.someMethod = function () { 9 | var interval; 10 | 11 | if (!slowObjectInstance) { 12 | slowObjectInstance = slowObject.init(); 13 | } else { 14 | slowObjectInstance.someMethod(); 15 | } 16 | 17 | interval = window.setInterval(invokeMethodWhenExists, 100); 18 | 19 | function invokeMethodWhenExists() { 20 | if (slowObjectInstance) { 21 | console.log('proxying some method'); 22 | window.clearInterval(interval); 23 | 24 | slowObjectInstance.someMethod(); 25 | } 26 | } 27 | } 28 | } 29 | 30 | return { 31 | init: function () { 32 | return new SlowObjectProxy(); 33 | } 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /js/pubsub/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var moduleA = require('pubsub/moduleA'), 8 | moduleB = require('pubsub/moduleB'); 9 | 10 | moduleB.publishEvent(); 11 | moduleB.publishEvent(); 12 | } 13 | 14 | }; 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /js/pubsub/moduleA.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var pubSub = require('pubsub/pubsub'), 5 | subscription; 6 | 7 | subscription = pubSub.subscribe('atopic', function (data) { 8 | console.log('atopic was published with data: ' + data.something); 9 | subscription.dispose(); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /js/pubsub/moduleB.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | var pubSub = require('pubsub/pubsub'); 5 | 6 | return { 7 | publishEvent: function () { 8 | var data = { 9 | something: 'some data' 10 | }; 11 | 12 | pubSub.publish('atopic', data); 13 | } 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /js/pubsub/pubsub.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var subscribers = {}; 5 | 6 | return { 7 | publish: function (topic, data) { 8 | if (!subscribers[topic]) { 9 | return; 10 | } 11 | 12 | subscribers[topic].forEach(function (subscriber) { 13 | subscriber(data); 14 | }); 15 | }, 16 | subscribe: function (topic, callback) { 17 | var index; 18 | 19 | if (!subscribers[topic]) { 20 | subscribers[topic] = []; 21 | } 22 | 23 | index = subscribers[topic].push(callback) - 1; 24 | 25 | return { 26 | dispose: function () { 27 | subscribers[topic].splice(index, 1); 28 | } 29 | } 30 | } 31 | }; 32 | }); 33 | -------------------------------------------------------------------------------- /js/strategy/emailValidator.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | return { 5 | validate: function (value) { 6 | return value.indexOf('@') !== -1; 7 | } 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /js/strategy/init.js: -------------------------------------------------------------------------------- 1 | define(function (require) { 2 | 'use strict'; 3 | 4 | return { 5 | init: function () { 6 | 7 | var Strategy = require('strategy/strategy'), 8 | telValidator = require('strategy/telValidator'), 9 | emailValidator = require('strategy/emailValidator'), 10 | validator; 11 | 12 | validator = new Strategy(); 13 | 14 | console.log(validator.selectValidator(telValidator).validate(012345678901)); 15 | 16 | console.log(validator.selectValidator(emailValidator).validate('test')); 17 | } 18 | 19 | }; 20 | 21 | }); 22 | -------------------------------------------------------------------------------- /js/strategy/strategy.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | var Validator = function () { }; 5 | 6 | Validator.prototype.selectValidator = function (validator) { 7 | this.validator = validator; 8 | 9 | return this; 10 | }; 11 | 12 | Validator.prototype.validate = function (value) { 13 | if (this.validator) { 14 | return this.validator.validate(value); 15 | } 16 | throw ('No validator selected'); 17 | }; 18 | 19 | return Validator; 20 | }); 21 | -------------------------------------------------------------------------------- /js/strategy/telValidator.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 'use strict'; 3 | 4 | return { 5 | validate: function (value) { 6 | return (/^[0-9]{11}$/g).test(value); 7 | } 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jspatterns", 3 | "version": "1.0.0", 4 | "description": "Code examples for the Put Design Patterns into Practice course from Tuts+", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "JavaScript", 11 | "design", 12 | "patterns" 13 | ], 14 | "author": "Dan Wellman (danwellman.co.uk)", 15 | "license": "MIT" 16 | } 17 | --------------------------------------------------------------------------------