├── .babelrc ├── .editorconfig ├── .gitignore ├── .istanbul.yml ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── docs.md ├── package.json ├── scripts ├── build-docs.js └── format.js ├── src ├── behavioral │ ├── chain-of-resp │ │ ├── chain-of-resp-es6.js │ │ └── chain-of-resp.js │ ├── command │ │ ├── command.js │ │ └── command_es6.js │ ├── interpreter │ │ ├── interpreter.js │ │ └── interpreter_es6.js │ ├── iterator │ │ ├── iterator.js │ │ └── iterator_es6.js │ ├── mediator │ │ ├── mediator.js │ │ └── mediator_es6.js │ ├── memento │ │ ├── memento.js │ │ └── memento_es6.js │ ├── observer │ │ ├── observer.js │ │ └── observer_es6.js │ ├── state │ │ ├── state.js │ │ └── state_es6.js │ ├── strategy │ │ ├── strategy.js │ │ └── strategy_es6.js │ ├── template │ │ ├── template.js │ │ └── template_es6.js │ └── visitor │ │ ├── visitor.js │ │ └── visitor_es6.js ├── creational │ ├── abstract-factory │ │ ├── abstract-factory.js │ │ └── abstract-factory_es6.js │ ├── builder │ │ ├── builder.js │ │ └── builder_es6.js │ ├── factory │ │ ├── factory.js │ │ └── factory_es6.js │ ├── prototype │ │ ├── prototype.js │ │ └── prototype_es6.js │ └── singleton │ │ ├── singleton.js │ │ └── singleton_es6.js └── structural │ ├── adapter │ ├── adapter.js │ └── adapter_es6.js │ ├── bridge │ ├── bridge.js │ └── bridge_es6.js │ ├── composite │ ├── composite.js │ └── composite_es6.js │ ├── decorator │ ├── decorator.js │ └── decorator_es6.js │ ├── facade │ ├── facade.js │ └── facade_es6.js │ ├── flyweight │ ├── flyweight.js │ └── flyweight_es6.js │ └── proxy │ ├── proxy.js │ └── proxy_es6.js └── test ├── abstract-factory-test.js ├── adapter-test.js ├── adapter_es6-test.js ├── bridge-test.js ├── bridge_es6-test.js ├── builder-test.js ├── builder_es6-test.js ├── chain-of-resp-es6-test.js ├── chain-of-resp-test.js ├── command-test.js ├── command_es6-test.js ├── composite-test.js ├── composite_es6-test.js ├── decorator-test.js ├── decorator_es6-test.js ├── facade-test.js ├── facade_es6-test.js ├── factory-test.js ├── factory_es6-test.js ├── flyweight-test.js ├── flyweight_es6-test.js ├── interpreter-test.js ├── interpreter_es6-test.js ├── iterator-test.js ├── mediator-test.js ├── mediator_es6-test.js ├── memento-test.js ├── memento_es6-test.js ├── observer-test.js ├── observer_es6-test.js ├── prototype-test.js ├── prototype_es6-test.js ├── proxy-test.js ├── proxy_es6-test.js ├── singleton-test.js ├── singleton_es6-test.js ├── state-test.js ├── state_es6-test.js ├── strategy-test.js ├── strategy_es6-test.js ├── template-test.js ├── template_es6-test.js ├── visitor-test.js └── visitor_es6-test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dea/ 2 | node_modules/ 3 | coverage/ 4 | *.swp 5 | *.swo 6 | /.idea 7 | -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | instrumentation: 2 | root: src 3 | extensions: 4 | - .js 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 50, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : false, // true: Identifiers must be in camelCase 10 | "curly" : false, // true: Require {} for every new block or scope 11 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 12 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 14 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "latedef" : false, // true: Require variables/functions to be defined before being used 16 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 17 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 18 | "noempty" : true, // true: Prohibit use of empty blocks 19 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 20 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 21 | "plusplus" : false, // true: Prohibit use of `++` and `--` 22 | "quotmark" : false, // Quotation mark consistency: 23 | // false : do nothing (default) 24 | // true : ensure whatever is used is consistent 25 | // "single" : require single quotes 26 | // "double" : require double quotes 27 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 28 | "unused" : true, // Unused variables: 29 | // true : all variables, last function parameter 30 | // "vars" : all variables only 31 | // "strict" : all variables, all function parameters 32 | "strict" : false, // true: Requires all functions run in ES5 Strict Mode 33 | "maxparams" : false, // {int} Max number of formal params allowed per function 34 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 35 | "maxstatements" : false, // {int} Max number statements per function 36 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 37 | "maxlen" : false, // {int} Max number of characters per line 38 | "varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed. 39 | 40 | // Relaxing 41 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 42 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 43 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 44 | "eqnull" : false, // true: Tolerate use of `== null` 45 | "esversion" : 6, // {int} Specify the ECMAScript version to which the code must adhere. 46 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 47 | // (ex: `for each`, multiple try/catch, function expression…) 48 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 49 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 50 | "funcscope" : false, // true: Tolerate defining variables inside control statements 51 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 52 | "iterator" : false, // true: Tolerate using the `__iterator__` property 53 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 54 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 55 | "laxcomma" : false, // true: Tolerate comma-first style coding 56 | "loopfunc" : false, // true: Tolerate functions being defined in loops 57 | "multistr" : false, // true: Tolerate multi-line strings 58 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 59 | "notypeof" : false, // true: Tolerate invalid typeof operator values 60 | "proto" : false, // true: Tolerate using the `__proto__` property 61 | "scripturl" : false, // true: Tolerate script-targeted URLs 62 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 63 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 64 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 65 | "validthis" : false, // true: Tolerate using this in a non-constructor function 66 | 67 | // Environments 68 | "browser" : true, // Web Browser (window, document, etc) 69 | "browserify" : false, // Browserify (node.js code in the browser) 70 | "couch" : false, // CouchDB 71 | "devel" : true, // Development/debugging (alert, confirm, etc) 72 | "dojo" : false, // Dojo Toolkit 73 | "jasmine" : false, // Jasmine 74 | "jquery" : false, // jQuery 75 | "mocha" : true, // Mocha 76 | "mootools" : false, // MooTools 77 | "node" : true, // Node.js 78 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 79 | "phantom" : false, // PhantomJS 80 | "prototypejs" : false, // Prototype and Scriptaculous 81 | "qunit" : false, // QUnit 82 | "rhino" : false, // Rhino 83 | "shelljs" : false, // ShellJS 84 | "typed" : false, // Globals for typed array constructions 85 | "worker" : false, // Web Workers 86 | "wsh" : false, // Windows Scripting Host 87 | "yui" : false, // Yahoo User Interface 88 | 89 | // Custom Globals 90 | "globals" : {} // additional predefined global variables 91 | } 92 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | after_success: 5 | - npm run coveralls 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Felipe Beline 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Design Patterns JS [![Coverage Status](https://coveralls.io/repos/github/FelipeBB/Design-Patterns-JS/badge.svg?branch=master)](https://coveralls.io/github/FelipeBB/Design-Patterns-JS?branch=master) 2 | 3 | Here you will find the 23 (GoF) design patterns implemented in JavaScript using both prototype and ES6 classes. You can use the [**docs.md**](docs.md) to quicky overview the examples. 4 | 5 | Follows the list of patterns separed by type: 6 | 7 | ### Creational Patterns 8 | Creational patterns are ones that create objects for you, rather than having you instantiate objects directly. This gives your program more flexibility in deciding which objects need to be created for a given case. 9 | 10 | - **Abstract factory:** provide an interface for creating families of related or dependent objects without specifying their concrete classes. 11 | - **Builder:** separate the construction of a complex object from its representation, allowing the same construction process to create various representations. 12 | - **Factory method:** define an interface for creating a single object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. 13 | - **Prototype:** specify the kinds of objects to create using a prototypical instance, and create new objects from the 'skeleton' of an existing object, thus boosting performance and keeping memory footprints to a minimum. 14 | - **Singleton:** ensure a class has only one instance, and provide a global point of access to it. 15 | 16 | ### Structural Patterns 17 | These concern class and object composition. They use inheritance to compose interfaces and define ways to compose objects to obtain new functionality. 18 | 19 | - **Adapter:** allows classes with incompatible interfaces to work together by wrapping its own interface around that of an already existing class. 20 | - **Bridge:** decouples an abstraction from its implementation so that the two can vary independently. 21 | - **Composite:** composes zero-or-more similar objects so that they can be manipulated as one object. 22 | - **Decorator:** dynamically adds/overrides behaviour in an existing method of an object. 23 | - **Facade:** provides a simplified interface to a large body of code. 24 | - **Flyweight:** reduces the cost of creating and manipulating a large number of similar objects. 25 | - **Proxy:** provides a placeholder for another object to control access, reduce cost, and reduce complexity. 26 | 27 | ### Behavioral Patterns 28 | Most of these design patterns are specifically concerned with communication between objects. 29 | 30 | - **Chain of responsibility:** delegates commands to a chain of processing objects. 31 | - **Command:** creates objects which encapsulate actions and parameters. 32 | - **Interpreter:** implements a specialized language. 33 | - **Iterator:** accesses the elements of an object sequentially without exposing its underlying representation. 34 | - **Mediator:** allows loose coupling between classes by being the only class that has detailed knowledge of their methods. 35 | - **Memento:** provides the ability to restore an object to its previous state (undo). 36 | - **Observer:** is a publish/subscribe pattern which allows a number of observer objects to see an event. 37 | - **State:** allows an object to alter its behavior when its internal state changes. 38 | - **Strategy:** allows one of a family of algorithms to be selected on-the-fly at runtime. 39 | - **Template:** method defines the skeleton of an algorithm as an abstract class, allowing its subclasses to provide concrete behavior. 40 | - **Visitor:** separates an algorithm from an object structure by moving the hierarchy of methods into one object. 41 | 42 | ## Testing 43 | To run the tests you just need to execute the following commands inside the project root folder: 44 | ```bash 45 | npm install 46 | npm test 47 | ``` 48 | 49 | ## TODO 50 | Some patterns still need to be implemented using ES6 classes. 51 | 52 | ## Notes 53 | - All the short definitions used in this documentation were extracted from [here](https://en.wikipedia.org/wiki/Design_Patterns). 54 | -------------------------------------------------------------------------------- /docs.md: -------------------------------------------------------------------------------- 1 | # Design Patterns JS 2 | 3 | **[Behavioral](#behavioral)** 4 | * [Chain Of Resp](#chain-of-resp) 5 | * [Command](#command) 6 | * [Interpreter](#interpreter) 7 | * [Iterator](#iterator) 8 | * [Mediator](#mediator) 9 | * [Memento](#memento) 10 | * [Observer](#observer) 11 | * [State](#state) 12 | * [Strategy](#strategy) 13 | * [Template](#template) 14 | * [Visitor](#visitor) 15 | 16 | **[Creational](#creational)** 17 | * [Abstract Factory](#abstract-factory) 18 | * [Builder](#builder) 19 | * [Factory](#factory) 20 | * [Prototype](#prototype) 21 | * [Singleton](#singleton) 22 | 23 | **[Structural](#structural)** 24 | * [Adapter](#adapter) 25 | * [Bridge](#bridge) 26 | * [Composite](#composite) 27 | * [Decorator](#decorator) 28 | * [Facade](#facade) 29 | * [Flyweight](#flyweight) 30 | * [Proxy](#proxy) 31 | 32 | 33 | 34 | ## behavioral 35 | ### Chain Of Resp 36 | ##### chain-of-resp-es6.js 37 | ```Javascript 38 | class ShoppingCart { 39 | 40 | constructor() { 41 | this.products = []; 42 | } 43 | 44 | addProduct(p) { 45 | this.products.push(p); 46 | }; 47 | } 48 | 49 | class Discount { 50 | 51 | calc(products) { 52 | let ndiscount = new NumberDiscount(); 53 | let pdiscount = new PriceDiscount(); 54 | let none = new NoneDiscount(); 55 | ndiscount.setNext(pdiscount); 56 | pdiscount.setNext(none); 57 | return ndiscount.exec(products); 58 | }; 59 | } 60 | 61 | class NumberDiscount { 62 | 63 | constructor() { 64 | this.next = null; 65 | } 66 | 67 | setNext(fn) { 68 | this.next = fn; 69 | }; 70 | 71 | exec(products) { 72 | let result = 0; 73 | if (products.length > 3) 74 | result = 0.05; 75 | 76 | return result + this.next.exec(products); 77 | }; 78 | } 79 | 80 | class PriceDiscount { 81 | 82 | constructor() { 83 | this.next = null; 84 | } 85 | 86 | setNext(fn) { 87 | this.next = fn; 88 | }; 89 | 90 | exec(products) { 91 | let result = 0; 92 | let total = products.reduce((a, b) => a + b); 93 | 94 | if (total >= 500) 95 | result = 0.1; 96 | 97 | return result + this.next.exec(products); 98 | }; 99 | } 100 | 101 | class NoneDiscount { 102 | exec() { 103 | return 0; 104 | }; 105 | } 106 | 107 | export { 108 | ShoppingCart, 109 | Discount 110 | }; 111 | 112 | ``` 113 | ##### chain-of-resp.js 114 | ```Javascript 115 | function ShoppingCart() { 116 | this.products = []; 117 | 118 | this.addProduct = function(p) { 119 | this.products.push(p); 120 | }; 121 | } 122 | 123 | function Discount() { 124 | this.calc = function(products) { 125 | var ndiscount = new NumberDiscount(); 126 | var pdiscount = new PriceDiscount(); 127 | var none = new NoneDiscount(); 128 | 129 | ndiscount.setNext(pdiscount); 130 | pdiscount.setNext(none); 131 | 132 | return ndiscount.exec(products); 133 | }; 134 | } 135 | 136 | function NumberDiscount() { 137 | this.next = null; 138 | this.setNext = function(fn) { 139 | this.next = fn; 140 | }; 141 | 142 | this.exec = function(products) { 143 | var result = 0; 144 | if (products.length > 3) 145 | result = 0.05; 146 | 147 | return result + this.next.exec(products); 148 | }; 149 | } 150 | 151 | function PriceDiscount() { 152 | this.next = null; 153 | 154 | this.setNext = function(fn) { 155 | this.next = fn; 156 | }; 157 | 158 | this.exec = function(products) { 159 | var result = 0; 160 | var total = products.reduce(function(a, b) { 161 | return a + b; 162 | }); 163 | 164 | if (total >= 500) 165 | result = 0.1; 166 | 167 | return result + this.next.exec(products); 168 | }; 169 | } 170 | 171 | function NoneDiscount() { 172 | this.exec = function() { 173 | return 0; 174 | }; 175 | } 176 | 177 | module.exports = [ShoppingCart, Discount]; 178 | 179 | ``` 180 | 181 | ### Command 182 | ##### command.js 183 | ```Javascript 184 | function Cockpit(command) { 185 | this.command = command; 186 | } 187 | 188 | Cockpit.prototype.execute = function() { 189 | this.command.execute(); 190 | }; 191 | 192 | function Turbine() { 193 | this.speed = 0; 194 | this.state = false; 195 | } 196 | 197 | Turbine.prototype.on = function() { 198 | this.state = true; 199 | this.speed = 100; 200 | }; 201 | 202 | Turbine.prototype.off = function() { 203 | this.speed = 0; 204 | this.state = false; 205 | }; 206 | 207 | Turbine.prototype.speedDown = function() { 208 | if (!this.state) return; 209 | 210 | this.speed -= 100; 211 | }; 212 | 213 | Turbine.prototype.speedUp = function() { 214 | if (!this.state) return; 215 | 216 | this.speed += 100; 217 | }; 218 | 219 | function OnCommand(turbine) { 220 | this.turbine = turbine; 221 | } 222 | 223 | OnCommand.prototype.execute = function() { 224 | this.turbine.on(); 225 | }; 226 | 227 | function OffCommand(turbine) { 228 | this.turbine = turbine; 229 | } 230 | 231 | OffCommand.prototype.execute = function() { 232 | this.turbine.off(); 233 | }; 234 | 235 | function SpeedUpCommand(turbine) { 236 | this.turbine = turbine; 237 | } 238 | 239 | SpeedUpCommand.prototype.execute = function() { 240 | this.turbine.speedUp(); 241 | }; 242 | 243 | function SpeedDownCommand(turbine) { 244 | this.turbine = turbine; 245 | } 246 | 247 | SpeedDownCommand.prototype.execute = function() { 248 | this.turbine.speedDown(); 249 | }; 250 | 251 | module.exports = [Cockpit, Turbine, OnCommand, OffCommand, SpeedUpCommand, SpeedDownCommand]; 252 | 253 | ``` 254 | ##### command_es6.js 255 | ```Javascript 256 | class Cockpit { 257 | 258 | constructor(command) { 259 | this.command = command; 260 | } 261 | 262 | execute() { 263 | this.command.execute(); 264 | } 265 | } 266 | 267 | class Turbine { 268 | 269 | constructor() { 270 | this.state = false; 271 | } 272 | 273 | on() { 274 | this.state = true; 275 | } 276 | 277 | off() { 278 | this.state = false; 279 | } 280 | } 281 | 282 | class OnCommand { 283 | 284 | constructor(turbine) { 285 | this.turbine = turbine; 286 | } 287 | 288 | execute() { 289 | this.turbine.on(); 290 | } 291 | } 292 | 293 | class OffCommand { 294 | 295 | constructor(turbine) { 296 | this.turbine = turbine; 297 | } 298 | 299 | execute() { 300 | this.turbine.off(); 301 | } 302 | } 303 | 304 | export { 305 | Cockpit, 306 | Turbine, 307 | OnCommand, 308 | OffCommand 309 | }; 310 | 311 | ``` 312 | 313 | ### Interpreter 314 | ##### interpreter.js 315 | ```Javascript 316 | function Sum(left, right) { 317 | this.left = left; 318 | this.right = right; 319 | } 320 | 321 | Sum.prototype.interpret = function() { 322 | return this.left.interpret() + this.right.interpret(); 323 | }; 324 | 325 | function Min(left, right) { 326 | this.left = left; 327 | this.right = right; 328 | } 329 | 330 | Min.prototype.interpret = function() { 331 | return this.left.interpret() - this.right.interpret(); 332 | }; 333 | 334 | function Num(val) { 335 | this.val = val; 336 | } 337 | 338 | Num.prototype.interpret = function() { 339 | return this.val; 340 | }; 341 | 342 | module.exports = [Num, Min, Sum]; 343 | 344 | ``` 345 | ##### interpreter_es6.js 346 | ```Javascript 347 | class Sum { 348 | 349 | constructor(left, right) { 350 | this.left = left; 351 | this.right = right; 352 | } 353 | 354 | interpret() { 355 | return this.left.interpret() + this.right.interpret(); 356 | } 357 | } 358 | 359 | class Min { 360 | 361 | constructor(left, right) { 362 | this.left = left; 363 | this.right = right; 364 | } 365 | 366 | interpret() { 367 | return this.left.interpret() - this.right.interpret(); 368 | } 369 | } 370 | 371 | class Num { 372 | 373 | constructor(val) { 374 | this.val = val; 375 | } 376 | 377 | interpret() { 378 | return this.val; 379 | } 380 | } 381 | 382 | export { 383 | Num, 384 | Min, 385 | Sum 386 | }; 387 | 388 | ``` 389 | 390 | ### Iterator 391 | ##### iterator.js 392 | ```Javascript 393 | function Iterator(el) { 394 | this.index = 0; 395 | this.elements = el; 396 | } 397 | 398 | Iterator.prototype = { 399 | next: function() { 400 | return this.elements[this.index++]; 401 | }, 402 | hasNext: function() { 403 | return this.index < this.elements.length; 404 | } 405 | }; 406 | 407 | module.exports = Iterator; 408 | 409 | ``` 410 | ##### iterator_es6.js 411 | ```Javascript 412 | class Iterator { 413 | 414 | constructor(el) { 415 | this.index = 0; 416 | this.elements = el; 417 | } 418 | 419 | next() { 420 | return this.elements[this.index++]; 421 | } 422 | 423 | hasNext() { 424 | return this.index < this.elements.length; 425 | } 426 | } 427 | 428 | export default Iterator; 429 | 430 | ``` 431 | 432 | ### Mediator 433 | ##### mediator.js 434 | ```Javascript 435 | function TrafficTower() { 436 | this.airplanes = []; 437 | } 438 | 439 | TrafficTower.prototype.requestPositions = function() { 440 | return this.airplanes.map(function(airplane) { 441 | return airplane.position; 442 | }); 443 | }; 444 | 445 | function Airplane(position, trafficTower) { 446 | this.position = position; 447 | this.trafficTower = trafficTower; 448 | this.trafficTower.airplanes.push(this); 449 | } 450 | 451 | Airplane.prototype.requestPositions = function() { 452 | return this.trafficTower.requestPositions(); 453 | }; 454 | 455 | module.exports = [TrafficTower, Airplane]; 456 | 457 | ``` 458 | ##### mediator_es6.js 459 | ```Javascript 460 | class TrafficTower { 461 | 462 | constructor() { 463 | this.airplanes = []; 464 | } 465 | 466 | requestPositions() { 467 | return this.airplanes.map(airplane => { 468 | return airplane.position; 469 | }); 470 | } 471 | } 472 | 473 | class Airplane { 474 | 475 | constructor(position, trafficTower) { 476 | this.position = position; 477 | this.trafficTower = trafficTower; 478 | this.trafficTower.airplanes.push(this); 479 | } 480 | 481 | requestPositions() { 482 | return this.trafficTower.requestPositions(); 483 | } 484 | } 485 | 486 | export { 487 | TrafficTower, 488 | Airplane 489 | }; 490 | 491 | ``` 492 | 493 | ### Memento 494 | ##### memento.js 495 | ```Javascript 496 | function Memento(value) { 497 | this.value = value; 498 | } 499 | 500 | var originator = { 501 | store: function(val) { 502 | return new Memento(val); 503 | }, 504 | restore: function(memento) { 505 | return memento.value; 506 | } 507 | }; 508 | 509 | function Caretaker() { 510 | this.values = []; 511 | } 512 | 513 | Caretaker.prototype.addMemento = function(memento) { 514 | this.values.push(memento); 515 | }; 516 | 517 | Caretaker.prototype.getMemento = function(index) { 518 | return this.values[index]; 519 | }; 520 | 521 | module.exports = [originator, Caretaker]; 522 | 523 | ``` 524 | ##### memento_es6.js 525 | ```Javascript 526 | class Memento { 527 | constructor(value) { 528 | this.value = value; 529 | } 530 | } 531 | 532 | const originator = { 533 | store: function(val) { 534 | return new Memento(val); 535 | }, 536 | restore: function(memento) { 537 | return memento.value; 538 | } 539 | }; 540 | 541 | class Caretaker { 542 | 543 | constructor() { 544 | this.values = []; 545 | } 546 | 547 | addMemento(memento) { 548 | this.values.push(memento); 549 | } 550 | 551 | getMemento(index) { 552 | return this.values[index]; 553 | } 554 | } 555 | 556 | export { 557 | originator, 558 | Caretaker 559 | }; 560 | 561 | ``` 562 | 563 | ### Observer 564 | ##### observer.js 565 | ```Javascript 566 | function Product() { 567 | this.price = 0; 568 | this.actions = []; 569 | } 570 | 571 | Product.prototype.setBasePrice = function(val) { 572 | this.price = val; 573 | this.notifyAll(); 574 | }; 575 | 576 | Product.prototype.register = function(observer) { 577 | this.actions.push(observer); 578 | }; 579 | 580 | Product.prototype.unregister = function(observer) { 581 | this.actions = this.actions.filter(function(el) { 582 | return el != observer; 583 | }); 584 | }; 585 | 586 | Product.prototype.notifyAll = function() { 587 | return this.actions.forEach(function(el) { 588 | el.update(this); 589 | }.bind(this)); 590 | }; 591 | 592 | var fees = { 593 | update: function(product) { 594 | product.price = product.price * 1.2; 595 | } 596 | }; 597 | 598 | var proft = { 599 | update: function(product) { 600 | product.price = product.price * 2; 601 | } 602 | }; 603 | 604 | module.exports = [Product, fees, proft]; 605 | 606 | ``` 607 | ##### observer_es6.js 608 | ```Javascript 609 | class Product { 610 | constructor() { 611 | this.price = 0; 612 | this.actions = []; 613 | } 614 | 615 | setBasePrice(val) { 616 | this.price = val; 617 | this.notifyAll(); 618 | } 619 | 620 | register(observer) { 621 | this.actions.push(observer); 622 | } 623 | 624 | unregister(observer) { 625 | this.actions = this.actions.filter(el => !(el instanceof observer)); 626 | } 627 | 628 | notifyAll() { 629 | return this.actions.forEach(el => el.update(this)); 630 | } 631 | } 632 | 633 | class Fees { 634 | update(product) { 635 | product.price = product.price * 1.2; 636 | } 637 | } 638 | 639 | class Proft { 640 | update(product) { 641 | product.price = product.price * 2; 642 | } 643 | } 644 | 645 | export { 646 | Product, 647 | Fees, 648 | Proft 649 | }; 650 | 651 | ``` 652 | 653 | ### State 654 | ##### state.js 655 | ```Javascript 656 | function Order() { 657 | this.state = new WaitingForPayment(); 658 | 659 | this.nextState = function() { 660 | this.state = this.state.next(); 661 | }; 662 | } 663 | 664 | function WaitingForPayment() { 665 | this.name = 'waitingForPayment'; 666 | this.next = function() { 667 | return new Shipping(); 668 | }; 669 | } 670 | 671 | function Shipping() { 672 | this.name = 'shipping'; 673 | this.next = function() { 674 | return new Delivered(); 675 | }; 676 | } 677 | 678 | function Delivered() { 679 | this.name = 'delivered'; 680 | this.next = function() { 681 | return this; 682 | }; 683 | } 684 | 685 | module.exports = Order; 686 | 687 | ``` 688 | ##### state_es6.js 689 | ```Javascript 690 | class OrderStatus { 691 | constructor(name, nextStatus) { 692 | this.name = name; 693 | this.nextStatus = nextStatus; 694 | } 695 | 696 | next() { 697 | return new this.nextStatus(); 698 | } 699 | } 700 | 701 | class WaitingForPayment extends OrderStatus { 702 | constructor() { 703 | super('waitingForPayment', Shipping); 704 | } 705 | } 706 | 707 | class Shipping extends OrderStatus { 708 | constructor() { 709 | super('shipping', Delivered); 710 | } 711 | } 712 | 713 | class Delivered extends OrderStatus { 714 | constructor() { 715 | super('delivered', Delivered); 716 | } 717 | } 718 | 719 | class Order { 720 | constructor() { 721 | this.state = new WaitingForPayment(); 722 | } 723 | 724 | nextState() { 725 | this.state = this.state.next(); 726 | }; 727 | } 728 | 729 | export default Order; 730 | 731 | ``` 732 | 733 | ### Strategy 734 | ##### strategy.js 735 | ```Javascript 736 | function ShoppingCart(discount) { 737 | this.discount = discount; 738 | this.amount = 0; 739 | } 740 | 741 | ShoppingCart.prototype.setAmount = function(amount) { 742 | this.amount = amount; 743 | }; 744 | 745 | ShoppingCart.prototype.checkout = function() { 746 | return this.discount(this.amount); 747 | }; 748 | 749 | function guestStrategy(amount) { 750 | return amount; 751 | } 752 | 753 | function regularStrategy(amount) { 754 | return amount * 0.9; 755 | } 756 | 757 | function premiumStrategy(amount) { 758 | return amount * 0.8; 759 | } 760 | 761 | module.exports = [ShoppingCart, guestStrategy, regularStrategy, premiumStrategy]; 762 | 763 | ``` 764 | ##### strategy_es6.js 765 | ```Javascript 766 | class ShoppingCart { 767 | 768 | constructor(discount) { 769 | this.discount = discount; 770 | this.amount = 0; 771 | } 772 | 773 | checkout() { 774 | return this.discount(this.amount); 775 | } 776 | 777 | setAmount(amount) { 778 | this.amount = amount; 779 | } 780 | } 781 | 782 | function guestStrategy(amount) { 783 | return amount; 784 | } 785 | 786 | function regularStrategy(amount) { 787 | return amount * 0.9; 788 | } 789 | 790 | function premiumStrategy(amount) { 791 | return amount * 0.8; 792 | } 793 | 794 | export { 795 | ShoppingCart, 796 | guestStrategy, 797 | regularStrategy, 798 | premiumStrategy 799 | }; 800 | 801 | ``` 802 | 803 | ### Template 804 | ##### template.js 805 | ```Javascript 806 | function Tax() {} 807 | 808 | Tax.prototype.calc = function(value) { 809 | if (value >= 1000) 810 | value = this.overThousand(value); 811 | 812 | return this.complementaryFee(value); 813 | }; 814 | 815 | Tax.prototype.complementaryFee = function(value) { 816 | return value + 10; 817 | }; 818 | 819 | function Tax1() {} 820 | Tax1.prototype = Object.create(Tax.prototype); 821 | 822 | Tax1.prototype.overThousand = function(value) { 823 | return value * 1.1; 824 | }; 825 | 826 | function Tax2() {} 827 | Tax2.prototype = Object.create(Tax.prototype); 828 | 829 | Tax2.prototype.overThousand = function(value) { 830 | return value * 1.2; 831 | }; 832 | 833 | module.exports = [Tax1, Tax2]; 834 | 835 | ``` 836 | ##### template_es6.js 837 | ```Javascript 838 | class Tax { 839 | 840 | calc(value) { 841 | if (value >= 1000) 842 | value = this.overThousand(value); 843 | 844 | return this.complementaryFee(value); 845 | } 846 | 847 | complementaryFee(value) { 848 | return value + 10; 849 | } 850 | 851 | } 852 | 853 | class Tax1 extends Tax { 854 | 855 | constructor() { 856 | super(); 857 | } 858 | 859 | overThousand(value) { 860 | return value * 1.1; 861 | } 862 | } 863 | 864 | class Tax2 extends Tax { 865 | 866 | constructor() { 867 | super(); 868 | } 869 | 870 | overThousand(value) { 871 | return value * 1.2; 872 | } 873 | } 874 | 875 | export { 876 | Tax1, 877 | Tax2 878 | }; 879 | 880 | ``` 881 | 882 | ### Visitor 883 | ##### visitor.js 884 | ```Javascript 885 | function bonusVisitor(employee) { 886 | if (employee instanceof Manager) 887 | employee.bonus = employee.salary * 2; 888 | if (employee instanceof Developer) 889 | employee.bonus = employee.salary; 890 | } 891 | 892 | function Employee() { 893 | this.bonus = 0; 894 | } 895 | 896 | Employee.prototype.accept = function(visitor) { 897 | visitor(this); 898 | }; 899 | 900 | function Manager(salary) { 901 | this.salary = salary; 902 | } 903 | 904 | Manager.prototype = Object.create(Employee.prototype); 905 | 906 | function Developer(salary) { 907 | this.salary = salary; 908 | } 909 | 910 | Developer.prototype = Object.create(Employee.prototype); 911 | 912 | module.exports = [Developer, Manager, bonusVisitor]; 913 | 914 | ``` 915 | ##### visitor_es6.js 916 | ```Javascript 917 | function bonusVisitor(employee) { 918 | if (employee instanceof Manager) 919 | employee.bonus = employee.salary * 2; 920 | if (employee instanceof Developer) 921 | employee.bonus = employee.salary; 922 | } 923 | 924 | class Employee { 925 | 926 | constructor(salary) { 927 | this.bonus = 0; 928 | this.salary = salary; 929 | } 930 | 931 | accept(visitor) { 932 | visitor(this); 933 | } 934 | } 935 | 936 | class Manager extends Employee { 937 | constructor(salary) { 938 | super(salary); 939 | } 940 | } 941 | 942 | class Developer extends Employee { 943 | constructor(salary) { 944 | super(salary); 945 | } 946 | } 947 | 948 | export { 949 | Developer, 950 | Manager, 951 | bonusVisitor 952 | }; 953 | 954 | ``` 955 | 956 | 957 | ## creational 958 | ### Abstract Factory 959 | ##### abstract-factory.js 960 | ```Javascript 961 | function droidProducer(kind) { 962 | if (kind === 'battle') return battleDroidFactory; 963 | return pilotDroidFactory; 964 | } 965 | 966 | function battleDroidFactory() { 967 | return new B1(); 968 | } 969 | 970 | function pilotDroidFactory() { 971 | return new Rx24(); 972 | } 973 | 974 | function B1() {} 975 | B1.prototype.info = function() { 976 | return "B1, Battle Droid"; 977 | }; 978 | 979 | function Rx24() {} 980 | Rx24.prototype.info = function() { 981 | return "Rx24, Pilot Droid"; 982 | }; 983 | 984 | module.exports = droidProducer; 985 | 986 | ``` 987 | ##### abstract-factory_es6.js 988 | ```Javascript 989 | function droidProducer(kind) { 990 | if (kind === 'battle') return battleDroidFactory; 991 | return pilotDroidFactory; 992 | } 993 | 994 | function battleDroidFactory() { 995 | return new B1(); 996 | } 997 | 998 | function pilotDroidFactory() { 999 | return new Rx24(); 1000 | } 1001 | 1002 | class B1 { 1003 | info() { 1004 | return "B1, Battle Droid"; 1005 | } 1006 | } 1007 | 1008 | class Rx24 { 1009 | info() { 1010 | return "Rx24, Pilot Droid"; 1011 | } 1012 | } 1013 | 1014 | export default droidProducer; 1015 | 1016 | ``` 1017 | 1018 | ### Builder 1019 | ##### builder.js 1020 | ```Javascript 1021 | function Request() { 1022 | this.url = ''; 1023 | this.method = ''; 1024 | this.payload = {}; 1025 | } 1026 | 1027 | function RequestBuilder() { 1028 | 1029 | this.request = new Request(); 1030 | 1031 | this.forUrl = function(url) { 1032 | this.request.url = url; 1033 | return this; 1034 | }; 1035 | 1036 | this.useMethod = function(method) { 1037 | this.request.method = method; 1038 | return this; 1039 | }; 1040 | 1041 | this.payload = function(payload) { 1042 | this.request.payload = payload; 1043 | return this; 1044 | }; 1045 | 1046 | this.build = function() { 1047 | return this.request; 1048 | }; 1049 | 1050 | } 1051 | 1052 | module.exports = RequestBuilder; 1053 | 1054 | ``` 1055 | ##### builder_es6.js 1056 | ```Javascript 1057 | class Request { 1058 | constructor() { 1059 | this.url = ''; 1060 | this.method = ''; 1061 | this.payload = {}; 1062 | } 1063 | } 1064 | 1065 | class RequestBuilder { 1066 | constructor() { 1067 | this.request = new Request(); 1068 | } 1069 | 1070 | forUrl(url) { 1071 | this.request.url = url; 1072 | return this; 1073 | } 1074 | 1075 | useMethod(method) { 1076 | this.request.method = method; 1077 | return this; 1078 | } 1079 | 1080 | payload(payload) { 1081 | this.request.payload = payload; 1082 | return this; 1083 | } 1084 | 1085 | build() { 1086 | return this.request; 1087 | } 1088 | 1089 | } 1090 | 1091 | export default RequestBuilder; 1092 | 1093 | ``` 1094 | 1095 | ### Factory 1096 | ##### factory.js 1097 | ```Javascript 1098 | function bmwFactory(type) { 1099 | if (type === 'X5') 1100 | return new Bmw(type, 108000, 300); 1101 | if (type === 'X6') 1102 | return new Bmw(type, 111000, 320); 1103 | } 1104 | 1105 | function Bmw(model, price, maxSpeed) { 1106 | this.model = model; 1107 | this.price = price; 1108 | this.maxSpeed = maxSpeed; 1109 | } 1110 | 1111 | module.exports = bmwFactory; 1112 | 1113 | ``` 1114 | ##### factory_es6.js 1115 | ```Javascript 1116 | class BmwFactory { 1117 | 1118 | static create(type) { 1119 | if (type === 'X5') 1120 | return new Bmw(type, 108000, 300); 1121 | if (type === 'X6') 1122 | return new Bmw(type, 111000, 320); 1123 | } 1124 | } 1125 | 1126 | class Bmw { 1127 | constructor(model, price, maxSpeed) { 1128 | this.model = model; 1129 | this.price = price; 1130 | this.maxSpeed = maxSpeed; 1131 | } 1132 | } 1133 | 1134 | export default BmwFactory; 1135 | 1136 | ``` 1137 | 1138 | ### Prototype 1139 | ##### prototype.js 1140 | ```Javascript 1141 | function Sheep(name, weight) { 1142 | this.name = name; 1143 | this.weight = weight; 1144 | } 1145 | 1146 | Sheep.prototype.clone = function() { 1147 | return new Sheep(this.name, this.weight); 1148 | }; 1149 | 1150 | module.exports = Sheep; 1151 | 1152 | ``` 1153 | ##### prototype_es6.js 1154 | ```Javascript 1155 | class Sheep { 1156 | 1157 | constructor(name, weight) { 1158 | this.name = name; 1159 | this.weight = weight; 1160 | } 1161 | 1162 | clone() { 1163 | return new Sheep(this.name, this.weight); 1164 | } 1165 | } 1166 | 1167 | export default Sheep; 1168 | 1169 | ``` 1170 | 1171 | ### Singleton 1172 | ##### singleton.js 1173 | ```Javascript 1174 | function Person() { 1175 | 1176 | if (typeof Person.instance === 'object') 1177 | return Person.instance; 1178 | 1179 | Person.instance = this; 1180 | 1181 | return this; 1182 | } 1183 | 1184 | module.exports = Person; 1185 | 1186 | ``` 1187 | ##### singleton_es6.js 1188 | ```Javascript 1189 | class Person { 1190 | constructor() { 1191 | if (typeof Person.instance === 'object') { 1192 | return Person.instance; 1193 | } 1194 | Person.instance = this; 1195 | return this; 1196 | } 1197 | } 1198 | 1199 | export default Person; 1200 | 1201 | ``` 1202 | 1203 | 1204 | ## structural 1205 | ### Adapter 1206 | ##### adapter.js 1207 | ```Javascript 1208 | function Soldier(lvl) { 1209 | this.lvl = lvl; 1210 | } 1211 | 1212 | Soldier.prototype.attack = function() { 1213 | return this.lvl * 1; 1214 | }; 1215 | 1216 | function Jedi(lvl) { 1217 | this.lvl = lvl; 1218 | } 1219 | 1220 | Jedi.prototype.attackWithSaber = function() { 1221 | return this.lvl * 100; 1222 | }; 1223 | 1224 | function JediAdapter(jedi) { 1225 | this.jedi = jedi; 1226 | } 1227 | 1228 | JediAdapter.prototype.attack = function() { 1229 | return this.jedi.attackWithSaber(); 1230 | }; 1231 | 1232 | module.exports = [Soldier, Jedi, JediAdapter]; 1233 | 1234 | ``` 1235 | ##### adapter_es6.js 1236 | ```Javascript 1237 | class Soldier { 1238 | constructor(level) { 1239 | this.level = level; 1240 | } 1241 | 1242 | attack() { 1243 | return this.level * 1; 1244 | } 1245 | } 1246 | 1247 | class Jedi { 1248 | constructor(level) { 1249 | this.level = level; 1250 | } 1251 | 1252 | attackWithSaber() { 1253 | return this.level * 100; 1254 | } 1255 | } 1256 | 1257 | class JediAdapter { 1258 | constructor(jedi) { 1259 | this.jedi = jedi; 1260 | } 1261 | 1262 | attack() { 1263 | return this.jedi.attackWithSaber(); 1264 | } 1265 | } 1266 | 1267 | export { 1268 | Soldier, 1269 | Jedi, 1270 | JediAdapter 1271 | }; 1272 | 1273 | ``` 1274 | 1275 | ### Bridge 1276 | ##### bridge.js 1277 | ```Javascript 1278 | function EpsonPrinter(ink) { 1279 | this.ink = ink(); 1280 | } 1281 | EpsonPrinter.prototype.print = function() { 1282 | return "Printer: Epson, Ink: " + this.ink; 1283 | }; 1284 | 1285 | function HPprinter(ink) { 1286 | this.ink = ink(); 1287 | } 1288 | HPprinter.prototype.print = function() { 1289 | return "Printer: HP, Ink: " + this.ink; 1290 | }; 1291 | 1292 | function acrylicInk() { 1293 | return "acrylic-based"; 1294 | } 1295 | 1296 | function alcoholInk() { 1297 | return "alcohol-based"; 1298 | } 1299 | 1300 | module.exports = [EpsonPrinter, HPprinter, acrylicInk, alcoholInk]; 1301 | 1302 | ``` 1303 | ##### bridge_es6.js 1304 | ```Javascript 1305 | class Printer { 1306 | constructor(ink) { 1307 | this.ink = ink; 1308 | } 1309 | } 1310 | 1311 | class EpsonPrinter extends Printer { 1312 | constructor(ink) { 1313 | super(ink); 1314 | } 1315 | 1316 | print() { 1317 | return "Printer: Epson, Ink: " + this.ink.get(); 1318 | } 1319 | } 1320 | 1321 | class HPprinter extends Printer { 1322 | constructor(ink) { 1323 | super(ink); 1324 | } 1325 | 1326 | print() { 1327 | return "Printer: HP, Ink: " + this.ink.get(); 1328 | } 1329 | } 1330 | 1331 | class Ink { 1332 | constructor(type) { 1333 | this.type = type; 1334 | } 1335 | get() { 1336 | return this.type; 1337 | } 1338 | } 1339 | 1340 | class AcrylicInk extends Ink { 1341 | constructor() { 1342 | super("acrylic-based"); 1343 | } 1344 | } 1345 | 1346 | class AlcoholInk extends Ink { 1347 | constructor() { 1348 | super("alcohol-based"); 1349 | } 1350 | } 1351 | 1352 | export { 1353 | EpsonPrinter, 1354 | HPprinter, 1355 | AcrylicInk, 1356 | AlcoholInk 1357 | }; 1358 | 1359 | ``` 1360 | 1361 | ### Composite 1362 | ##### composite.js 1363 | ```Javascript 1364 | // composition 1365 | function EquipmentComposition(name) { 1366 | this.equipments = []; 1367 | this.name = name; 1368 | } 1369 | 1370 | EquipmentComposition.prototype.add = function(equipment) { 1371 | this.equipments.push(equipment); 1372 | }; 1373 | 1374 | EquipmentComposition.prototype.getPrice = function() { 1375 | return this.equipments.map(function(equipment) { 1376 | return equipment.getPrice(); 1377 | }).reduce(function(a, b) { 1378 | return a + b; 1379 | }); 1380 | }; 1381 | 1382 | function Equipment() {} 1383 | 1384 | Equipment.prototype.getPrice = function() { 1385 | return this.price; 1386 | }; 1387 | 1388 | // -- leafs 1389 | function FloppyDisk() { 1390 | this.name = "Floppy Disk"; 1391 | this.price = 70; 1392 | } 1393 | FloppyDisk.prototype = Object.create(Equipment.prototype); 1394 | 1395 | function HardDrive() { 1396 | this.name = "Hard Drive"; 1397 | this.price = 250; 1398 | } 1399 | HardDrive.prototype = Object.create(Equipment.prototype); 1400 | 1401 | function Memory() { 1402 | this.name = "8gb memomry"; 1403 | this.price = 280; 1404 | } 1405 | Memory.prototype = Object.create(Equipment.prototype); 1406 | 1407 | module.exports = [EquipmentComposition, FloppyDisk, HardDrive, Memory]; 1408 | 1409 | ``` 1410 | ##### composite_es6.js 1411 | ```Javascript 1412 | //Equipment 1413 | class Equipment { 1414 | 1415 | getPrice() { 1416 | return this.price || 0; 1417 | } 1418 | 1419 | getName() { 1420 | return this.name; 1421 | } 1422 | 1423 | setName(name) { 1424 | this.name = name; 1425 | } 1426 | } 1427 | 1428 | // --- composite --- 1429 | class Composite extends Equipment { 1430 | 1431 | constructor() { 1432 | super(); 1433 | this.equipments = []; 1434 | } 1435 | 1436 | add(equipment) { 1437 | this.equipments.push(equipment); 1438 | } 1439 | 1440 | getPrice() { 1441 | return this.equipments.map(equipment => { 1442 | return equipment.getPrice(); 1443 | }).reduce((a, b) => { 1444 | return a + b; 1445 | }); 1446 | } 1447 | } 1448 | 1449 | class Cabinet extends Composite { 1450 | constructor() { 1451 | super(); 1452 | this.setName('cabinet'); 1453 | } 1454 | } 1455 | 1456 | // --- leafs --- 1457 | class FloppyDisk extends Equipment { 1458 | constructor() { 1459 | super(); 1460 | this.setName('Floppy Disk'); 1461 | this.price = 70; 1462 | } 1463 | } 1464 | 1465 | class HardDrive extends Equipment { 1466 | constructor() { 1467 | super(); 1468 | this.setName('Hard Drive'); 1469 | this.price = 250; 1470 | } 1471 | } 1472 | 1473 | class Memory extends Equipment { 1474 | constructor() { 1475 | super(); 1476 | this.setName('Memory'); 1477 | this.price = 280; 1478 | } 1479 | } 1480 | 1481 | export { 1482 | Cabinet, 1483 | FloppyDisk, 1484 | HardDrive, 1485 | Memory 1486 | }; 1487 | 1488 | ``` 1489 | 1490 | ### Decorator 1491 | ##### decorator.js 1492 | ```Javascript 1493 | function Pasta() { 1494 | this.price = 0; 1495 | } 1496 | Pasta.prototype.getPrice = function() { 1497 | return this.price; 1498 | }; 1499 | 1500 | function Penne() { 1501 | this.price = 8; 1502 | } 1503 | Penne.prototype = Object.create(Pasta.prototype); 1504 | 1505 | function SauceDecorator(pasta) { 1506 | this.pasta = pasta; 1507 | } 1508 | 1509 | SauceDecorator.prototype.getPrice = function() { 1510 | return this.pasta.getPrice() + 5; 1511 | }; 1512 | 1513 | function CheeseDecorator(pasta) { 1514 | this.pasta = pasta; 1515 | } 1516 | 1517 | CheeseDecorator.prototype.getPrice = function() { 1518 | return this.pasta.getPrice() + 3; 1519 | }; 1520 | 1521 | module.exports = [Penne, SauceDecorator, CheeseDecorator]; 1522 | 1523 | ``` 1524 | ##### decorator_es6.js 1525 | ```Javascript 1526 | class Pasta { 1527 | constructor() { 1528 | this.price = 0; 1529 | } 1530 | getPrice() { 1531 | return this.price; 1532 | } 1533 | } 1534 | 1535 | class Penne extends Pasta { 1536 | constructor() { 1537 | super(); 1538 | this.price = 8; 1539 | } 1540 | } 1541 | 1542 | class PastaDecorator extends Pasta { 1543 | constructor(pasta) { 1544 | super(); 1545 | this.pasta = pasta; 1546 | } 1547 | 1548 | getPrice() { 1549 | return this.pasta.getPrice(); 1550 | } 1551 | } 1552 | 1553 | class SauceDecorator extends PastaDecorator { 1554 | constructor(pasta) { 1555 | super(pasta); 1556 | } 1557 | 1558 | getPrice() { 1559 | return super.getPrice() + 5; 1560 | } 1561 | } 1562 | 1563 | class CheeseDecorator extends PastaDecorator { 1564 | constructor(pasta) { 1565 | super(pasta); 1566 | } 1567 | 1568 | getPrice() { 1569 | return super.getPrice() + 3; 1570 | } 1571 | } 1572 | 1573 | export { 1574 | Penne, 1575 | SauceDecorator, 1576 | CheeseDecorator 1577 | }; 1578 | 1579 | ``` 1580 | 1581 | ### Facade 1582 | ##### facade.js 1583 | ```Javascript 1584 | var shopFacade = { 1585 | calc: function(price) { 1586 | price = discount(price); 1587 | price = fees(price); 1588 | price += shipping(); 1589 | return price; 1590 | } 1591 | }; 1592 | 1593 | function discount(value) { 1594 | return value * 0.9; 1595 | } 1596 | 1597 | function shipping() { 1598 | return 5; 1599 | } 1600 | 1601 | function fees(value) { 1602 | return value * 1.05; 1603 | } 1604 | 1605 | module.exports = shopFacade; 1606 | 1607 | ``` 1608 | ##### facade_es6.js 1609 | ```Javascript 1610 | class ShopFacade { 1611 | constructor() { 1612 | this.discount = new Discount(); 1613 | this.shipping = new Shipping(); 1614 | this.fees = new Fees(); 1615 | } 1616 | 1617 | calc(price) { 1618 | price = this.discount.calc(price); 1619 | price = this.fees.calc(price); 1620 | price += this.shipping.calc(); 1621 | return price; 1622 | } 1623 | } 1624 | 1625 | class Discount { 1626 | 1627 | calc(value) { 1628 | return value * 0.9; 1629 | } 1630 | } 1631 | 1632 | class Shipping { 1633 | calc() { 1634 | return 5; 1635 | } 1636 | } 1637 | 1638 | class Fees { 1639 | 1640 | calc(value) { 1641 | return value * 1.05; 1642 | } 1643 | } 1644 | 1645 | export default ShopFacade; 1646 | 1647 | ``` 1648 | 1649 | ### Flyweight 1650 | ##### flyweight.js 1651 | ```Javascript 1652 | function Color(name) { 1653 | this.name = name; 1654 | } 1655 | 1656 | var colorFactory = { 1657 | colors: {}, 1658 | create: function(name) { 1659 | var color = this.colors[name]; 1660 | if (color) return color; 1661 | 1662 | this.colors[name] = new Color(name); 1663 | return this.colors[name]; 1664 | } 1665 | }; 1666 | 1667 | module.exports = colorFactory; 1668 | 1669 | ``` 1670 | ##### flyweight_es6.js 1671 | ```Javascript 1672 | class Color { 1673 | constructor(name) { 1674 | this.name = name 1675 | } 1676 | } 1677 | 1678 | class colorFactory { 1679 | constructor(name) { 1680 | this.colors = {}; 1681 | } 1682 | create(name) { 1683 | let color = this.colors[name]; 1684 | if (color) return color; 1685 | this.colors[name] = new Color(name); 1686 | return this.colors[name]; 1687 | } 1688 | }; 1689 | 1690 | export { 1691 | colorFactory 1692 | }; 1693 | 1694 | ``` 1695 | 1696 | ### Proxy 1697 | ##### proxy.js 1698 | ```Javascript 1699 | function Car() { 1700 | this.drive = function() { 1701 | return "driving"; 1702 | }; 1703 | } 1704 | 1705 | function CarProxy(driver) { 1706 | this.driver = driver; 1707 | this.drive = function() { 1708 | if (driver.age < 18) 1709 | return "too young to drive"; 1710 | return new Car().drive(); 1711 | }; 1712 | } 1713 | 1714 | function Driver(age) { 1715 | this.age = age; 1716 | } 1717 | 1718 | module.exports = [Car, CarProxy, Driver]; 1719 | 1720 | ``` 1721 | ##### proxy_es6.js 1722 | ```Javascript 1723 | class Car { 1724 | drive() { 1725 | return "driving"; 1726 | }; 1727 | } 1728 | 1729 | class CarProxy { 1730 | constructor(driver) { 1731 | this.driver = driver; 1732 | } 1733 | drive() { 1734 | return (this.driver.age < 18) ? "too young to drive" : new Car().drive(); 1735 | }; 1736 | } 1737 | 1738 | class Driver { 1739 | constructor(age) { 1740 | this.age = age; 1741 | } 1742 | } 1743 | 1744 | export { 1745 | Car, 1746 | CarProxy, 1747 | Driver 1748 | }; 1749 | 1750 | ``` 1751 | 1752 | 1753 | 1754 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gof-js", 3 | "version": "1.0.0", 4 | "description": "Design patterns implemented in javascript", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "build-docs": "node ./scripts/build-docs.js", 11 | "format": "node ./scripts/format.js", 12 | "test": "mocha test --compilers js:babel-core/register", 13 | "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- 'test/**/*.js' --compilers js:babel-core/register", 14 | "coveralls": "npm run coverage && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", 15 | "build": "npm run test; npm run format; npm run build-docs" 16 | }, 17 | "devDependencies": { 18 | "babel-core": "^6.25.0", 19 | "babel-preset-es2015": "^6.24.1", 20 | "chai": "^3.5.0", 21 | "coveralls": "^2.11.16", 22 | "fs-extra-promise": "^0.4.1", 23 | "glob": "^7.1.3", 24 | "istanbul": "^1.0.0-alpha", 25 | "js-beautify": "^1.9.0", 26 | "mocha": "^2.4.5", 27 | "mocha-lcov-reporter": "^1.2.0" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/FelipeBB/GoF-JS.git" 32 | }, 33 | "keywords": [ 34 | "design", 35 | "patterns", 36 | "gof" 37 | ], 38 | "author": "felipe baravieira", 39 | "license": "MIT", 40 | "bugs": { 41 | "url": "https://github.com/FelipeBB/GoF-JS/issues" 42 | }, 43 | "homepage": "https://github.com/FelipeBB/GoF-JS#readme" 44 | } 45 | -------------------------------------------------------------------------------- /scripts/build-docs.js: -------------------------------------------------------------------------------- 1 | const nPath = require('path'); 2 | const fs = require('fs-extra-promise'); 3 | 4 | /* Path Constants */ 5 | const SOURCE_DIR = './src'; 6 | const TEST_DIR = './test'; 7 | const DOCS_FILE = './docs.md'; 8 | 9 | /* Data Storage */ 10 | const sections = []; 11 | 12 | // A map of file paths to contents 13 | // e.g. { 'src/ex.js' : 'console.log("test")' } 14 | const files = new Map(); 15 | 16 | 17 | /* Building Data Types */ 18 | const makeSection = ( name ) => ({ 19 | name, 20 | path: `${SOURCE_DIR}/${name}`, 21 | topics: [], 22 | }); 23 | 24 | const makeTopic = ( sectionName ) => ( name ) => ({ 25 | name, 26 | path: `${SOURCE_DIR}/${sectionName}/${name}`, 27 | files: [], 28 | tests: [], 29 | }); 30 | 31 | /* Utility functions */ 32 | const getFileContents = ( path ) => fs.existsAsync( path ) 33 | .then( exists => exists ? fs.readFileAsync( path, 'utf-8' ) : '' ); 34 | 35 | const stripPunctuation = ( str ) => 36 | str.replace( /["'.,\/#!$%\^&\*;:’{}=_`~()]/g, '' ); 37 | 38 | const dashCase = ( str ) => stripPunctuation( str ).toLowerCase().split( ' ' ).filter( 39 | ( word ) => word !== '' 40 | ).join( '-' ); 41 | 42 | const reverseDashCase= (str) => str.split('-').map( 43 | word => word.replace( 44 | /(?:^\w|[a-z]|\b\w)/g, 45 | ( letter, index ) => 46 | index == 0 ? 47 | letter.toUpperCase() : 48 | letter.toLowerCase() 49 | ) 50 | ).join(' '); 51 | 52 | /* Writing Markdown */ 53 | const writeDocument = () => 54 | `${writeHeader(sections)} 55 | ${sections.map( writeSection ).join('')} 56 | `; 57 | 58 | const writeHeader = ( sections ) => 59 | `# Design Patterns JS 60 | 61 | ${sections.map( writeSectionContents ).join('')} 62 | `; 63 | 64 | const writeSectionContents = ({ name, topics }) => 65 | `**[${reverseDashCase(name)}](#${dashCase(name)})** 66 | ${topics.map( writeTopicContents ).join('')} 67 | `; 68 | 69 | const writeTopicContents = ({ name }) => `* [${reverseDashCase(name)}](#${dashCase(name)}) 70 | `; 71 | 72 | const writeSection = ({ name, path, topics }) => 73 | `## ${name} 74 | ${topics.map( writeTopic ).join('')} 75 | `; 76 | 77 | const writeTopic = ({ name, path, files, tests }) => 78 | `### ${reverseDashCase(name)} 79 | ${files.map( writeFile ).join('')} 80 | `; 81 | 82 | const writeFile = ( path ) => 83 | `##### ${nPath.basename(path)} 84 | \`\`\`Javascript 85 | ${files.get(path)} 86 | \`\`\` 87 | `; 88 | 89 | /* File System Actions */ 90 | fs.readdirAsync( SOURCE_DIR ) 91 | .then( sectionNames => { 92 | 93 | // Make sections from section names and store. 94 | sections.push( ...sectionNames.map( makeSection ) ); 95 | 96 | // Get topics of each section. 97 | return Promise.all( sections.map( 98 | ({ path }) => fs.readdirAsync( path ) 99 | ) ); 100 | 101 | } ) 102 | .then( topicsBySection => { 103 | 104 | // Set topics of each section. 105 | topicsBySection.forEach( 106 | ( topics, i ) => sections[i].topics = topics.map( makeTopic( sections[i].name ) ) 107 | ); 108 | 109 | // Get the files inside of each topic 110 | return Promise.all( sections.map( 111 | section => Promise.all( section.topics.map( 112 | ({ path }) => fs.readdirAsync( path ) 113 | ) ) 114 | ) ); 115 | 116 | } ) 117 | .then( filesByTopicBySection => { 118 | 119 | filesByTopicBySection.forEach( 120 | ( filesByTopic, sectionIndex ) => filesByTopic.forEach( 121 | ( topicFiles, topicIndex ) => { 122 | const { path, topics } = sections[sectionIndex]; 123 | const topic = topics[topicIndex]; 124 | 125 | // Store files in each topic 126 | topic.files = topicFiles.map( 127 | name => `${path}/${topic.name}/${name}` 128 | ); 129 | 130 | // Store test files for each file 131 | topic.tests = topicFiles.map( 132 | path => `${TEST_DIR}/${nPath.basename(path,'.js')}-test.js` 133 | ); 134 | 135 | // Give each file a default value in the file map. 136 | [ ...topic.files, ...topic.tests ].forEach( 137 | filePath => files.set( filePath, '' ) 138 | ); 139 | 140 | } 141 | ) 142 | ); 143 | 144 | // Create array of paths. 145 | const filePaths = [ ...files.keys() ]; 146 | 147 | // Get contents of each file. 148 | return Promise.all( filePaths.map( getFileContents ) ); 149 | 150 | } ) 151 | .then( fileContents => { 152 | 153 | // Create array of paths. 154 | const filePaths = [ ...files.keys() ]; 155 | 156 | fileContents.forEach( 157 | ( contents, i ) => files.set( filePaths[i], contents ) 158 | ); 159 | 160 | return fs.outputFileAsync( DOCS_FILE, writeDocument() ) 161 | 162 | } ) 163 | .catch( error => { throw error; } ) 164 | 165 | -------------------------------------------------------------------------------- /scripts/format.js: -------------------------------------------------------------------------------- 1 | const jsBeautify = require('js-beautify')['js_beautify']; 2 | const fs = require('fs'); 3 | const glob = require('glob'); 4 | 5 | const options = { 6 | indent_size: 2, 7 | indent_char: ' ', 8 | indent_with_tabs: false, 9 | eol: '\n', 10 | end_with_newline: true, 11 | indent_level: 0, 12 | preserve_newlines: true, 13 | max_preserve_newlines: 2, 14 | space_in_paren: false, 15 | space_in_empty_paren: false, 16 | jslint_happy: false, 17 | space_after_anon_function: false, 18 | brace_style: 'collapse', 19 | break_chained_methods: false, 20 | keep_array_indentation: false, 21 | unescape_strings: false, 22 | wrap_line_length: 0, 23 | e4x: false, 24 | comma_first: false, 25 | operator_position: 'before-newline' 26 | }; 27 | 28 | glob('**/*(src|test)/**/*.js', { absolute: true }, (er, files) => { 29 | files.forEach(file => { 30 | console.log(`js-beautify ${file}`); 31 | const data = fs.readFileSync(file, 'utf8'); 32 | const nextData = jsBeautify(data, options); 33 | fs.writeFileSync(file, nextData, 'utf8'); 34 | }); 35 | }); -------------------------------------------------------------------------------- /src/behavioral/chain-of-resp/chain-of-resp-es6.js: -------------------------------------------------------------------------------- 1 | class ShoppingCart { 2 | 3 | constructor() { 4 | this.products = []; 5 | } 6 | 7 | addProduct(p) { 8 | this.products.push(p); 9 | }; 10 | } 11 | 12 | class Discount { 13 | 14 | calc(products) { 15 | let ndiscount = new NumberDiscount(); 16 | let pdiscount = new PriceDiscount(); 17 | let none = new NoneDiscount(); 18 | ndiscount.setNext(pdiscount); 19 | pdiscount.setNext(none); 20 | return ndiscount.exec(products); 21 | }; 22 | } 23 | 24 | class NumberDiscount { 25 | 26 | constructor() { 27 | this.next = null; 28 | } 29 | 30 | setNext(fn) { 31 | this.next = fn; 32 | }; 33 | 34 | exec(products) { 35 | let result = 0; 36 | if (products.length > 3) 37 | result = 0.05; 38 | 39 | return result + this.next.exec(products); 40 | }; 41 | } 42 | 43 | class PriceDiscount { 44 | 45 | constructor() { 46 | this.next = null; 47 | } 48 | 49 | setNext(fn) { 50 | this.next = fn; 51 | }; 52 | 53 | exec(products) { 54 | let result = 0; 55 | let total = products.reduce((a, b) => a + b); 56 | 57 | if (total >= 500) 58 | result = 0.1; 59 | 60 | return result + this.next.exec(products); 61 | }; 62 | } 63 | 64 | class NoneDiscount { 65 | exec() { 66 | return 0; 67 | }; 68 | } 69 | 70 | export { 71 | ShoppingCart, 72 | Discount 73 | }; 74 | -------------------------------------------------------------------------------- /src/behavioral/chain-of-resp/chain-of-resp.js: -------------------------------------------------------------------------------- 1 | function ShoppingCart() { 2 | this.products = []; 3 | 4 | this.addProduct = function(p) { 5 | this.products.push(p); 6 | }; 7 | } 8 | 9 | function Discount() { 10 | this.calc = function(products) { 11 | var ndiscount = new NumberDiscount(); 12 | var pdiscount = new PriceDiscount(); 13 | var none = new NoneDiscount(); 14 | 15 | ndiscount.setNext(pdiscount); 16 | pdiscount.setNext(none); 17 | 18 | return ndiscount.exec(products); 19 | }; 20 | } 21 | 22 | function NumberDiscount() { 23 | this.next = null; 24 | this.setNext = function(fn) { 25 | this.next = fn; 26 | }; 27 | 28 | this.exec = function(products) { 29 | var result = 0; 30 | if (products.length > 3) 31 | result = 0.05; 32 | 33 | return result + this.next.exec(products); 34 | }; 35 | } 36 | 37 | function PriceDiscount() { 38 | this.next = null; 39 | 40 | this.setNext = function(fn) { 41 | this.next = fn; 42 | }; 43 | 44 | this.exec = function(products) { 45 | var result = 0; 46 | var total = products.reduce(function(a, b) { 47 | return a + b; 48 | }); 49 | 50 | if (total >= 500) 51 | result = 0.1; 52 | 53 | return result + this.next.exec(products); 54 | }; 55 | } 56 | 57 | function NoneDiscount() { 58 | this.exec = function() { 59 | return 0; 60 | }; 61 | } 62 | 63 | module.exports = [ShoppingCart, Discount]; 64 | -------------------------------------------------------------------------------- /src/behavioral/command/command.js: -------------------------------------------------------------------------------- 1 | function Cockpit(command) { 2 | this.command = command; 3 | } 4 | 5 | Cockpit.prototype.execute = function() { 6 | this.command.execute(); 7 | }; 8 | 9 | function Turbine() { 10 | this.speed = 0; 11 | this.state = false; 12 | } 13 | 14 | Turbine.prototype.on = function() { 15 | this.state = true; 16 | this.speed = 100; 17 | }; 18 | 19 | Turbine.prototype.off = function() { 20 | this.speed = 0; 21 | this.state = false; 22 | }; 23 | 24 | Turbine.prototype.speedDown = function() { 25 | if (!this.state) return; 26 | 27 | this.speed -= 100; 28 | }; 29 | 30 | Turbine.prototype.speedUp = function() { 31 | if (!this.state) return; 32 | 33 | this.speed += 100; 34 | }; 35 | 36 | function OnCommand(turbine) { 37 | this.turbine = turbine; 38 | } 39 | 40 | OnCommand.prototype.execute = function() { 41 | this.turbine.on(); 42 | }; 43 | 44 | function OffCommand(turbine) { 45 | this.turbine = turbine; 46 | } 47 | 48 | OffCommand.prototype.execute = function() { 49 | this.turbine.off(); 50 | }; 51 | 52 | function SpeedUpCommand(turbine) { 53 | this.turbine = turbine; 54 | } 55 | 56 | SpeedUpCommand.prototype.execute = function() { 57 | this.turbine.speedUp(); 58 | }; 59 | 60 | function SpeedDownCommand(turbine) { 61 | this.turbine = turbine; 62 | } 63 | 64 | SpeedDownCommand.prototype.execute = function() { 65 | this.turbine.speedDown(); 66 | }; 67 | 68 | module.exports = [Cockpit, Turbine, OnCommand, OffCommand, SpeedUpCommand, SpeedDownCommand]; 69 | -------------------------------------------------------------------------------- /src/behavioral/command/command_es6.js: -------------------------------------------------------------------------------- 1 | class Cockpit { 2 | 3 | constructor(command) { 4 | this.command = command; 5 | } 6 | 7 | execute() { 8 | this.command.execute(); 9 | } 10 | } 11 | 12 | class Turbine { 13 | 14 | constructor() { 15 | this.state = false; 16 | } 17 | 18 | on() { 19 | this.state = true; 20 | } 21 | 22 | off() { 23 | this.state = false; 24 | } 25 | } 26 | 27 | class OnCommand { 28 | 29 | constructor(turbine) { 30 | this.turbine = turbine; 31 | } 32 | 33 | execute() { 34 | this.turbine.on(); 35 | } 36 | } 37 | 38 | class OffCommand { 39 | 40 | constructor(turbine) { 41 | this.turbine = turbine; 42 | } 43 | 44 | execute() { 45 | this.turbine.off(); 46 | } 47 | } 48 | 49 | export { 50 | Cockpit, 51 | Turbine, 52 | OnCommand, 53 | OffCommand 54 | }; 55 | -------------------------------------------------------------------------------- /src/behavioral/interpreter/interpreter.js: -------------------------------------------------------------------------------- 1 | function Sum(left, right) { 2 | this.left = left; 3 | this.right = right; 4 | } 5 | 6 | Sum.prototype.interpret = function() { 7 | return this.left.interpret() + this.right.interpret(); 8 | }; 9 | 10 | function Min(left, right) { 11 | this.left = left; 12 | this.right = right; 13 | } 14 | 15 | Min.prototype.interpret = function() { 16 | return this.left.interpret() - this.right.interpret(); 17 | }; 18 | 19 | function Num(val) { 20 | this.val = val; 21 | } 22 | 23 | Num.prototype.interpret = function() { 24 | return this.val; 25 | }; 26 | 27 | module.exports = [Num, Min, Sum]; 28 | -------------------------------------------------------------------------------- /src/behavioral/interpreter/interpreter_es6.js: -------------------------------------------------------------------------------- 1 | class Sum { 2 | 3 | constructor(left, right) { 4 | this.left = left; 5 | this.right = right; 6 | } 7 | 8 | interpret() { 9 | return this.left.interpret() + this.right.interpret(); 10 | } 11 | } 12 | 13 | class Min { 14 | 15 | constructor(left, right) { 16 | this.left = left; 17 | this.right = right; 18 | } 19 | 20 | interpret() { 21 | return this.left.interpret() - this.right.interpret(); 22 | } 23 | } 24 | 25 | class Num { 26 | 27 | constructor(val) { 28 | this.val = val; 29 | } 30 | 31 | interpret() { 32 | return this.val; 33 | } 34 | } 35 | 36 | export { 37 | Num, 38 | Min, 39 | Sum 40 | }; 41 | -------------------------------------------------------------------------------- /src/behavioral/iterator/iterator.js: -------------------------------------------------------------------------------- 1 | function Iterator(el) { 2 | this.index = 0; 3 | this.elements = el; 4 | } 5 | 6 | Iterator.prototype = { 7 | next: function() { 8 | return this.elements[this.index++]; 9 | }, 10 | hasNext: function() { 11 | return this.index < this.elements.length; 12 | } 13 | }; 14 | 15 | module.exports = Iterator; 16 | -------------------------------------------------------------------------------- /src/behavioral/iterator/iterator_es6.js: -------------------------------------------------------------------------------- 1 | class Iterator { 2 | 3 | constructor(el) { 4 | this.index = 0; 5 | this.elements = el; 6 | } 7 | 8 | next() { 9 | return this.elements[this.index++]; 10 | } 11 | 12 | hasNext() { 13 | return this.index < this.elements.length; 14 | } 15 | } 16 | 17 | export default Iterator; 18 | -------------------------------------------------------------------------------- /src/behavioral/mediator/mediator.js: -------------------------------------------------------------------------------- 1 | function TrafficTower() { 2 | this.airplanes = []; 3 | } 4 | 5 | TrafficTower.prototype.requestPositions = function() { 6 | return this.airplanes.map(function(airplane) { 7 | return airplane.position; 8 | }); 9 | }; 10 | 11 | function Airplane(position, trafficTower) { 12 | this.position = position; 13 | this.trafficTower = trafficTower; 14 | this.trafficTower.airplanes.push(this); 15 | } 16 | 17 | Airplane.prototype.requestPositions = function() { 18 | return this.trafficTower.requestPositions(); 19 | }; 20 | 21 | module.exports = [TrafficTower, Airplane]; 22 | -------------------------------------------------------------------------------- /src/behavioral/mediator/mediator_es6.js: -------------------------------------------------------------------------------- 1 | class TrafficTower { 2 | 3 | constructor() { 4 | this.airplanes = []; 5 | } 6 | 7 | requestPositions() { 8 | return this.airplanes.map(airplane => { 9 | return airplane.position; 10 | }); 11 | } 12 | } 13 | 14 | class Airplane { 15 | 16 | constructor(position, trafficTower) { 17 | this.position = position; 18 | this.trafficTower = trafficTower; 19 | this.trafficTower.airplanes.push(this); 20 | } 21 | 22 | requestPositions() { 23 | return this.trafficTower.requestPositions(); 24 | } 25 | } 26 | 27 | export { 28 | TrafficTower, 29 | Airplane 30 | }; 31 | -------------------------------------------------------------------------------- /src/behavioral/memento/memento.js: -------------------------------------------------------------------------------- 1 | function Memento(value) { 2 | this.value = value; 3 | } 4 | 5 | var originator = { 6 | store: function(val) { 7 | return new Memento(val); 8 | }, 9 | restore: function(memento) { 10 | return memento.value; 11 | } 12 | }; 13 | 14 | function Caretaker() { 15 | this.values = []; 16 | } 17 | 18 | Caretaker.prototype.addMemento = function(memento) { 19 | this.values.push(memento); 20 | }; 21 | 22 | Caretaker.prototype.getMemento = function(index) { 23 | return this.values[index]; 24 | }; 25 | 26 | module.exports = [originator, Caretaker]; 27 | -------------------------------------------------------------------------------- /src/behavioral/memento/memento_es6.js: -------------------------------------------------------------------------------- 1 | class Memento { 2 | constructor(value) { 3 | this.value = value; 4 | } 5 | } 6 | 7 | const originator = { 8 | store: function(val) { 9 | return new Memento(val); 10 | }, 11 | restore: function(memento) { 12 | return memento.value; 13 | } 14 | }; 15 | 16 | class Caretaker { 17 | 18 | constructor() { 19 | this.values = []; 20 | } 21 | 22 | addMemento(memento) { 23 | this.values.push(memento); 24 | } 25 | 26 | getMemento(index) { 27 | return this.values[index]; 28 | } 29 | } 30 | 31 | export { 32 | originator, 33 | Caretaker 34 | }; 35 | -------------------------------------------------------------------------------- /src/behavioral/observer/observer.js: -------------------------------------------------------------------------------- 1 | function Product() { 2 | this.price = 0; 3 | this.actions = []; 4 | } 5 | 6 | Product.prototype.setBasePrice = function(val) { 7 | this.price = val; 8 | this.notifyAll(); 9 | }; 10 | 11 | Product.prototype.register = function(observer) { 12 | this.actions.push(observer); 13 | }; 14 | 15 | Product.prototype.unregister = function(observer) { 16 | this.actions = this.actions.filter(function(el) { 17 | return el != observer; 18 | }); 19 | }; 20 | 21 | Product.prototype.notifyAll = function() { 22 | return this.actions.forEach(function(el) { 23 | el.update(this); 24 | }.bind(this)); 25 | }; 26 | 27 | var fees = { 28 | update: function(product) { 29 | product.price = product.price * 1.2; 30 | } 31 | }; 32 | 33 | var proft = { 34 | update: function(product) { 35 | product.price = product.price * 2; 36 | } 37 | }; 38 | 39 | module.exports = [Product, fees, proft]; 40 | -------------------------------------------------------------------------------- /src/behavioral/observer/observer_es6.js: -------------------------------------------------------------------------------- 1 | class Product { 2 | constructor() { 3 | this.price = 0; 4 | this.actions = []; 5 | } 6 | 7 | setBasePrice(val) { 8 | this.price = val; 9 | this.notifyAll(); 10 | } 11 | 12 | register(observer) { 13 | this.actions.push(observer); 14 | } 15 | 16 | unregister(observer) { 17 | this.actions = this.actions.filter(el => !(el instanceof observer)); 18 | } 19 | 20 | notifyAll() { 21 | return this.actions.forEach(el => el.update(this)); 22 | } 23 | } 24 | 25 | class Fees { 26 | update(product) { 27 | product.price = product.price * 1.2; 28 | } 29 | } 30 | 31 | class Proft { 32 | update(product) { 33 | product.price = product.price * 2; 34 | } 35 | } 36 | 37 | export { 38 | Product, 39 | Fees, 40 | Proft 41 | }; 42 | -------------------------------------------------------------------------------- /src/behavioral/state/state.js: -------------------------------------------------------------------------------- 1 | function Order() { 2 | this.state = new WaitingForPayment(); 3 | 4 | this.nextState = function() { 5 | this.state = this.state.next(); 6 | }; 7 | } 8 | 9 | function WaitingForPayment() { 10 | this.name = 'waitingForPayment'; 11 | this.next = function() { 12 | return new Shipping(); 13 | }; 14 | } 15 | 16 | function Shipping() { 17 | this.name = 'shipping'; 18 | this.next = function() { 19 | return new Delivered(); 20 | }; 21 | } 22 | 23 | function Delivered() { 24 | this.name = 'delivered'; 25 | this.next = function() { 26 | return this; 27 | }; 28 | } 29 | 30 | module.exports = Order; 31 | -------------------------------------------------------------------------------- /src/behavioral/state/state_es6.js: -------------------------------------------------------------------------------- 1 | class OrderStatus { 2 | constructor(name, nextStatus) { 3 | this.name = name; 4 | this.nextStatus = nextStatus; 5 | } 6 | 7 | next() { 8 | return new this.nextStatus(); 9 | } 10 | } 11 | 12 | class WaitingForPayment extends OrderStatus { 13 | constructor() { 14 | super('waitingForPayment', Shipping); 15 | } 16 | } 17 | 18 | class Shipping extends OrderStatus { 19 | constructor() { 20 | super('shipping', Delivered); 21 | } 22 | } 23 | 24 | class Delivered extends OrderStatus { 25 | constructor() { 26 | super('delivered', Delivered); 27 | } 28 | } 29 | 30 | class Order { 31 | constructor() { 32 | this.state = new WaitingForPayment(); 33 | } 34 | 35 | nextState() { 36 | this.state = this.state.next(); 37 | }; 38 | } 39 | 40 | export default Order; 41 | -------------------------------------------------------------------------------- /src/behavioral/strategy/strategy.js: -------------------------------------------------------------------------------- 1 | function ShoppingCart(discount) { 2 | this.discount = discount; 3 | this.amount = 0; 4 | } 5 | 6 | ShoppingCart.prototype.setAmount = function(amount) { 7 | this.amount = amount; 8 | }; 9 | 10 | ShoppingCart.prototype.checkout = function() { 11 | return this.discount(this.amount); 12 | }; 13 | 14 | function guestStrategy(amount) { 15 | return amount; 16 | } 17 | 18 | function regularStrategy(amount) { 19 | return amount * 0.9; 20 | } 21 | 22 | function premiumStrategy(amount) { 23 | return amount * 0.8; 24 | } 25 | 26 | module.exports = [ShoppingCart, guestStrategy, regularStrategy, premiumStrategy]; 27 | -------------------------------------------------------------------------------- /src/behavioral/strategy/strategy_es6.js: -------------------------------------------------------------------------------- 1 | class ShoppingCart { 2 | 3 | constructor(discount) { 4 | this.discount = discount; 5 | this.amount = 0; 6 | } 7 | 8 | checkout() { 9 | return this.discount(this.amount); 10 | } 11 | 12 | setAmount(amount) { 13 | this.amount = amount; 14 | } 15 | } 16 | 17 | function guestStrategy(amount) { 18 | return amount; 19 | } 20 | 21 | function regularStrategy(amount) { 22 | return amount * 0.9; 23 | } 24 | 25 | function premiumStrategy(amount) { 26 | return amount * 0.8; 27 | } 28 | 29 | export { 30 | ShoppingCart, 31 | guestStrategy, 32 | regularStrategy, 33 | premiumStrategy 34 | }; 35 | -------------------------------------------------------------------------------- /src/behavioral/template/template.js: -------------------------------------------------------------------------------- 1 | function Tax() {} 2 | 3 | Tax.prototype.calc = function(value) { 4 | if (value >= 1000) 5 | value = this.overThousand(value); 6 | 7 | return this.complementaryFee(value); 8 | }; 9 | 10 | Tax.prototype.complementaryFee = function(value) { 11 | return value + 10; 12 | }; 13 | 14 | function Tax1() {} 15 | Tax1.prototype = Object.create(Tax.prototype); 16 | 17 | Tax1.prototype.overThousand = function(value) { 18 | return value * 1.1; 19 | }; 20 | 21 | function Tax2() {} 22 | Tax2.prototype = Object.create(Tax.prototype); 23 | 24 | Tax2.prototype.overThousand = function(value) { 25 | return value * 1.2; 26 | }; 27 | 28 | module.exports = [Tax1, Tax2]; 29 | -------------------------------------------------------------------------------- /src/behavioral/template/template_es6.js: -------------------------------------------------------------------------------- 1 | class Tax { 2 | 3 | calc(value) { 4 | if (value >= 1000) 5 | value = this.overThousand(value); 6 | 7 | return this.complementaryFee(value); 8 | } 9 | 10 | complementaryFee(value) { 11 | return value + 10; 12 | } 13 | 14 | } 15 | 16 | class Tax1 extends Tax { 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | overThousand(value) { 23 | return value * 1.1; 24 | } 25 | } 26 | 27 | class Tax2 extends Tax { 28 | 29 | constructor() { 30 | super(); 31 | } 32 | 33 | overThousand(value) { 34 | return value * 1.2; 35 | } 36 | } 37 | 38 | export { 39 | Tax1, 40 | Tax2 41 | }; 42 | -------------------------------------------------------------------------------- /src/behavioral/visitor/visitor.js: -------------------------------------------------------------------------------- 1 | function bonusVisitor(employee) { 2 | if (employee instanceof Manager) 3 | employee.bonus = employee.salary * 2; 4 | if (employee instanceof Developer) 5 | employee.bonus = employee.salary; 6 | } 7 | 8 | function Employee() { 9 | this.bonus = 0; 10 | } 11 | 12 | Employee.prototype.accept = function(visitor) { 13 | visitor(this); 14 | }; 15 | 16 | function Manager(salary) { 17 | this.salary = salary; 18 | } 19 | 20 | Manager.prototype = Object.create(Employee.prototype); 21 | 22 | function Developer(salary) { 23 | this.salary = salary; 24 | } 25 | 26 | Developer.prototype = Object.create(Employee.prototype); 27 | 28 | module.exports = [Developer, Manager, bonusVisitor]; 29 | -------------------------------------------------------------------------------- /src/behavioral/visitor/visitor_es6.js: -------------------------------------------------------------------------------- 1 | function bonusVisitor(employee) { 2 | if (employee instanceof Manager) 3 | employee.bonus = employee.salary * 2; 4 | if (employee instanceof Developer) 5 | employee.bonus = employee.salary; 6 | } 7 | 8 | class Employee { 9 | 10 | constructor(salary) { 11 | this.bonus = 0; 12 | this.salary = salary; 13 | } 14 | 15 | accept(visitor) { 16 | visitor(this); 17 | } 18 | } 19 | 20 | class Manager extends Employee { 21 | constructor(salary) { 22 | super(salary); 23 | } 24 | } 25 | 26 | class Developer extends Employee { 27 | constructor(salary) { 28 | super(salary); 29 | } 30 | } 31 | 32 | export { 33 | Developer, 34 | Manager, 35 | bonusVisitor 36 | }; 37 | -------------------------------------------------------------------------------- /src/creational/abstract-factory/abstract-factory.js: -------------------------------------------------------------------------------- 1 | function droidProducer(kind) { 2 | if (kind === 'battle') return battleDroidFactory; 3 | return pilotDroidFactory; 4 | } 5 | 6 | function battleDroidFactory() { 7 | return new B1(); 8 | } 9 | 10 | function pilotDroidFactory() { 11 | return new Rx24(); 12 | } 13 | 14 | function B1() {} 15 | B1.prototype.info = function() { 16 | return "B1, Battle Droid"; 17 | }; 18 | 19 | function Rx24() {} 20 | Rx24.prototype.info = function() { 21 | return "Rx24, Pilot Droid"; 22 | }; 23 | 24 | module.exports = droidProducer; 25 | -------------------------------------------------------------------------------- /src/creational/abstract-factory/abstract-factory_es6.js: -------------------------------------------------------------------------------- 1 | function droidProducer(kind) { 2 | if (kind === 'battle') return battleDroidFactory; 3 | return pilotDroidFactory; 4 | } 5 | 6 | function battleDroidFactory() { 7 | return new B1(); 8 | } 9 | 10 | function pilotDroidFactory() { 11 | return new Rx24(); 12 | } 13 | 14 | class B1 { 15 | info() { 16 | return "B1, Battle Droid"; 17 | } 18 | } 19 | 20 | class Rx24 { 21 | info() { 22 | return "Rx24, Pilot Droid"; 23 | } 24 | } 25 | 26 | export default droidProducer; 27 | -------------------------------------------------------------------------------- /src/creational/builder/builder.js: -------------------------------------------------------------------------------- 1 | function Request() { 2 | this.url = ''; 3 | this.method = ''; 4 | this.payload = {}; 5 | } 6 | 7 | function RequestBuilder() { 8 | 9 | this.request = new Request(); 10 | 11 | this.forUrl = function(url) { 12 | this.request.url = url; 13 | return this; 14 | }; 15 | 16 | this.useMethod = function(method) { 17 | this.request.method = method; 18 | return this; 19 | }; 20 | 21 | this.payload = function(payload) { 22 | this.request.payload = payload; 23 | return this; 24 | }; 25 | 26 | this.build = function() { 27 | return this.request; 28 | }; 29 | 30 | } 31 | 32 | module.exports = RequestBuilder; 33 | -------------------------------------------------------------------------------- /src/creational/builder/builder_es6.js: -------------------------------------------------------------------------------- 1 | class Request { 2 | constructor() { 3 | this.url = ''; 4 | this.method = ''; 5 | this.payload = {}; 6 | } 7 | } 8 | 9 | class RequestBuilder { 10 | constructor() { 11 | this.request = new Request(); 12 | } 13 | 14 | forUrl(url) { 15 | this.request.url = url; 16 | return this; 17 | } 18 | 19 | useMethod(method) { 20 | this.request.method = method; 21 | return this; 22 | } 23 | 24 | payload(payload) { 25 | this.request.payload = payload; 26 | return this; 27 | } 28 | 29 | build() { 30 | return this.request; 31 | } 32 | 33 | } 34 | 35 | export default RequestBuilder; 36 | -------------------------------------------------------------------------------- /src/creational/factory/factory.js: -------------------------------------------------------------------------------- 1 | function bmwFactory(type) { 2 | if (type === 'X5') 3 | return new Bmw(type, 108000, 300); 4 | if (type === 'X6') 5 | return new Bmw(type, 111000, 320); 6 | } 7 | 8 | function Bmw(model, price, maxSpeed) { 9 | this.model = model; 10 | this.price = price; 11 | this.maxSpeed = maxSpeed; 12 | } 13 | 14 | module.exports = bmwFactory; 15 | -------------------------------------------------------------------------------- /src/creational/factory/factory_es6.js: -------------------------------------------------------------------------------- 1 | class BmwFactory { 2 | 3 | static create(type) { 4 | if (type === 'X5') 5 | return new Bmw(type, 108000, 300); 6 | if (type === 'X6') 7 | return new Bmw(type, 111000, 320); 8 | } 9 | } 10 | 11 | class Bmw { 12 | constructor(model, price, maxSpeed) { 13 | this.model = model; 14 | this.price = price; 15 | this.maxSpeed = maxSpeed; 16 | } 17 | } 18 | 19 | export default BmwFactory; 20 | -------------------------------------------------------------------------------- /src/creational/prototype/prototype.js: -------------------------------------------------------------------------------- 1 | function Sheep(name, weight) { 2 | this.name = name; 3 | this.weight = weight; 4 | } 5 | 6 | Sheep.prototype.clone = function() { 7 | return new Sheep(this.name, this.weight); 8 | }; 9 | 10 | module.exports = Sheep; 11 | -------------------------------------------------------------------------------- /src/creational/prototype/prototype_es6.js: -------------------------------------------------------------------------------- 1 | class Sheep { 2 | 3 | constructor(name, weight) { 4 | this.name = name; 5 | this.weight = weight; 6 | } 7 | 8 | clone() { 9 | return new Sheep(this.name, this.weight); 10 | } 11 | } 12 | 13 | export default Sheep; 14 | -------------------------------------------------------------------------------- /src/creational/singleton/singleton.js: -------------------------------------------------------------------------------- 1 | function Person() { 2 | 3 | if (typeof Person.instance === 'object') 4 | return Person.instance; 5 | 6 | Person.instance = this; 7 | 8 | return this; 9 | } 10 | 11 | module.exports = Person; 12 | -------------------------------------------------------------------------------- /src/creational/singleton/singleton_es6.js: -------------------------------------------------------------------------------- 1 | class Person { 2 | constructor() { 3 | if (typeof Person.instance === 'object') { 4 | return Person.instance; 5 | } 6 | Person.instance = this; 7 | return this; 8 | } 9 | } 10 | 11 | export default Person; 12 | -------------------------------------------------------------------------------- /src/structural/adapter/adapter.js: -------------------------------------------------------------------------------- 1 | function Soldier(lvl) { 2 | this.lvl = lvl; 3 | } 4 | 5 | Soldier.prototype.attack = function() { 6 | return this.lvl * 1; 7 | }; 8 | 9 | function Jedi(lvl) { 10 | this.lvl = lvl; 11 | } 12 | 13 | Jedi.prototype.attackWithSaber = function() { 14 | return this.lvl * 100; 15 | }; 16 | 17 | function JediAdapter(jedi) { 18 | this.jedi = jedi; 19 | } 20 | 21 | JediAdapter.prototype.attack = function() { 22 | return this.jedi.attackWithSaber(); 23 | }; 24 | 25 | module.exports = [Soldier, Jedi, JediAdapter]; 26 | -------------------------------------------------------------------------------- /src/structural/adapter/adapter_es6.js: -------------------------------------------------------------------------------- 1 | class Soldier { 2 | constructor(level) { 3 | this.level = level; 4 | } 5 | 6 | attack() { 7 | return this.level * 1; 8 | } 9 | } 10 | 11 | class Jedi { 12 | constructor(level) { 13 | this.level = level; 14 | } 15 | 16 | attackWithSaber() { 17 | return this.level * 100; 18 | } 19 | } 20 | 21 | class JediAdapter { 22 | constructor(jedi) { 23 | this.jedi = jedi; 24 | } 25 | 26 | attack() { 27 | return this.jedi.attackWithSaber(); 28 | } 29 | } 30 | 31 | export { 32 | Soldier, 33 | Jedi, 34 | JediAdapter 35 | }; 36 | -------------------------------------------------------------------------------- /src/structural/bridge/bridge.js: -------------------------------------------------------------------------------- 1 | function EpsonPrinter(ink) { 2 | this.ink = ink(); 3 | } 4 | EpsonPrinter.prototype.print = function() { 5 | return "Printer: Epson, Ink: " + this.ink; 6 | }; 7 | 8 | function HPprinter(ink) { 9 | this.ink = ink(); 10 | } 11 | HPprinter.prototype.print = function() { 12 | return "Printer: HP, Ink: " + this.ink; 13 | }; 14 | 15 | function acrylicInk() { 16 | return "acrylic-based"; 17 | } 18 | 19 | function alcoholInk() { 20 | return "alcohol-based"; 21 | } 22 | 23 | module.exports = [EpsonPrinter, HPprinter, acrylicInk, alcoholInk]; 24 | -------------------------------------------------------------------------------- /src/structural/bridge/bridge_es6.js: -------------------------------------------------------------------------------- 1 | class Printer { 2 | constructor(ink) { 3 | this.ink = ink; 4 | } 5 | } 6 | 7 | class EpsonPrinter extends Printer { 8 | constructor(ink) { 9 | super(ink); 10 | } 11 | 12 | print() { 13 | return "Printer: Epson, Ink: " + this.ink.get(); 14 | } 15 | } 16 | 17 | class HPprinter extends Printer { 18 | constructor(ink) { 19 | super(ink); 20 | } 21 | 22 | print() { 23 | return "Printer: HP, Ink: " + this.ink.get(); 24 | } 25 | } 26 | 27 | class Ink { 28 | constructor(type) { 29 | this.type = type; 30 | } 31 | get() { 32 | return this.type; 33 | } 34 | } 35 | 36 | class AcrylicInk extends Ink { 37 | constructor() { 38 | super("acrylic-based"); 39 | } 40 | } 41 | 42 | class AlcoholInk extends Ink { 43 | constructor() { 44 | super("alcohol-based"); 45 | } 46 | } 47 | 48 | export { 49 | EpsonPrinter, 50 | HPprinter, 51 | AcrylicInk, 52 | AlcoholInk 53 | }; 54 | -------------------------------------------------------------------------------- /src/structural/composite/composite.js: -------------------------------------------------------------------------------- 1 | // composition 2 | function EquipmentComposition(name) { 3 | this.equipments = []; 4 | this.name = name; 5 | } 6 | 7 | EquipmentComposition.prototype.add = function(equipment) { 8 | this.equipments.push(equipment); 9 | }; 10 | 11 | EquipmentComposition.prototype.getPrice = function() { 12 | return this.equipments.map(function(equipment) { 13 | return equipment.getPrice(); 14 | }).reduce(function(a, b) { 15 | return a + b; 16 | }); 17 | }; 18 | 19 | function Equipment() {} 20 | 21 | Equipment.prototype.getPrice = function() { 22 | return this.price; 23 | }; 24 | 25 | // -- leafs 26 | function FloppyDisk() { 27 | this.name = "Floppy Disk"; 28 | this.price = 70; 29 | } 30 | FloppyDisk.prototype = Object.create(Equipment.prototype); 31 | 32 | function HardDrive() { 33 | this.name = "Hard Drive"; 34 | this.price = 250; 35 | } 36 | HardDrive.prototype = Object.create(Equipment.prototype); 37 | 38 | function Memory() { 39 | this.name = "8gb memomry"; 40 | this.price = 280; 41 | } 42 | Memory.prototype = Object.create(Equipment.prototype); 43 | 44 | module.exports = [EquipmentComposition, FloppyDisk, HardDrive, Memory]; 45 | -------------------------------------------------------------------------------- /src/structural/composite/composite_es6.js: -------------------------------------------------------------------------------- 1 | //Equipment 2 | class Equipment { 3 | 4 | getPrice() { 5 | return this.price || 0; 6 | } 7 | 8 | getName() { 9 | return this.name; 10 | } 11 | 12 | setName(name) { 13 | this.name = name; 14 | } 15 | } 16 | 17 | // --- composite --- 18 | class Composite extends Equipment { 19 | 20 | constructor() { 21 | super(); 22 | this.equipments = []; 23 | } 24 | 25 | add(equipment) { 26 | this.equipments.push(equipment); 27 | } 28 | 29 | getPrice() { 30 | return this.equipments.map(equipment => { 31 | return equipment.getPrice(); 32 | }).reduce((a, b) => { 33 | return a + b; 34 | }); 35 | } 36 | } 37 | 38 | class Cabinet extends Composite { 39 | constructor() { 40 | super(); 41 | this.setName('cabinet'); 42 | } 43 | } 44 | 45 | // --- leafs --- 46 | class FloppyDisk extends Equipment { 47 | constructor() { 48 | super(); 49 | this.setName('Floppy Disk'); 50 | this.price = 70; 51 | } 52 | } 53 | 54 | class HardDrive extends Equipment { 55 | constructor() { 56 | super(); 57 | this.setName('Hard Drive'); 58 | this.price = 250; 59 | } 60 | } 61 | 62 | class Memory extends Equipment { 63 | constructor() { 64 | super(); 65 | this.setName('Memory'); 66 | this.price = 280; 67 | } 68 | } 69 | 70 | export { 71 | Cabinet, 72 | FloppyDisk, 73 | HardDrive, 74 | Memory 75 | }; 76 | -------------------------------------------------------------------------------- /src/structural/decorator/decorator.js: -------------------------------------------------------------------------------- 1 | function Pasta() { 2 | this.price = 0; 3 | } 4 | Pasta.prototype.getPrice = function() { 5 | return this.price; 6 | }; 7 | 8 | function Penne() { 9 | this.price = 8; 10 | } 11 | Penne.prototype = Object.create(Pasta.prototype); 12 | 13 | function SauceDecorator(pasta) { 14 | this.pasta = pasta; 15 | } 16 | 17 | SauceDecorator.prototype.getPrice = function() { 18 | return this.pasta.getPrice() + 5; 19 | }; 20 | 21 | function CheeseDecorator(pasta) { 22 | this.pasta = pasta; 23 | } 24 | 25 | CheeseDecorator.prototype.getPrice = function() { 26 | return this.pasta.getPrice() + 3; 27 | }; 28 | 29 | module.exports = [Penne, SauceDecorator, CheeseDecorator]; 30 | -------------------------------------------------------------------------------- /src/structural/decorator/decorator_es6.js: -------------------------------------------------------------------------------- 1 | class Pasta { 2 | constructor() { 3 | this.price = 0; 4 | } 5 | getPrice() { 6 | return this.price; 7 | } 8 | } 9 | 10 | class Penne extends Pasta { 11 | constructor() { 12 | super(); 13 | this.price = 8; 14 | } 15 | } 16 | 17 | class PastaDecorator extends Pasta { 18 | constructor(pasta) { 19 | super(); 20 | this.pasta = pasta; 21 | } 22 | 23 | getPrice() { 24 | return this.pasta.getPrice(); 25 | } 26 | } 27 | 28 | class SauceDecorator extends PastaDecorator { 29 | constructor(pasta) { 30 | super(pasta); 31 | } 32 | 33 | getPrice() { 34 | return super.getPrice() + 5; 35 | } 36 | } 37 | 38 | class CheeseDecorator extends PastaDecorator { 39 | constructor(pasta) { 40 | super(pasta); 41 | } 42 | 43 | getPrice() { 44 | return super.getPrice() + 3; 45 | } 46 | } 47 | 48 | export { 49 | Penne, 50 | SauceDecorator, 51 | CheeseDecorator 52 | }; 53 | -------------------------------------------------------------------------------- /src/structural/facade/facade.js: -------------------------------------------------------------------------------- 1 | var shopFacade = { 2 | calc: function(price) { 3 | price = discount(price); 4 | price = fees(price); 5 | price += shipping(); 6 | return price; 7 | } 8 | }; 9 | 10 | function discount(value) { 11 | return value * 0.9; 12 | } 13 | 14 | function shipping() { 15 | return 5; 16 | } 17 | 18 | function fees(value) { 19 | return value * 1.05; 20 | } 21 | 22 | module.exports = shopFacade; 23 | -------------------------------------------------------------------------------- /src/structural/facade/facade_es6.js: -------------------------------------------------------------------------------- 1 | class ShopFacade { 2 | constructor() { 3 | this.discount = new Discount(); 4 | this.shipping = new Shipping(); 5 | this.fees = new Fees(); 6 | } 7 | 8 | calc(price) { 9 | price = this.discount.calc(price); 10 | price = this.fees.calc(price); 11 | price += this.shipping.calc(); 12 | return price; 13 | } 14 | } 15 | 16 | class Discount { 17 | 18 | calc(value) { 19 | return value * 0.9; 20 | } 21 | } 22 | 23 | class Shipping { 24 | calc() { 25 | return 5; 26 | } 27 | } 28 | 29 | class Fees { 30 | 31 | calc(value) { 32 | return value * 1.05; 33 | } 34 | } 35 | 36 | export default ShopFacade; 37 | -------------------------------------------------------------------------------- /src/structural/flyweight/flyweight.js: -------------------------------------------------------------------------------- 1 | function Color(name) { 2 | this.name = name; 3 | } 4 | 5 | var colorFactory = { 6 | colors: {}, 7 | create: function(name) { 8 | var color = this.colors[name]; 9 | if (color) return color; 10 | 11 | this.colors[name] = new Color(name); 12 | return this.colors[name]; 13 | } 14 | }; 15 | 16 | module.exports = colorFactory; 17 | -------------------------------------------------------------------------------- /src/structural/flyweight/flyweight_es6.js: -------------------------------------------------------------------------------- 1 | class Color { 2 | constructor(name) { 3 | this.name = name 4 | } 5 | } 6 | 7 | class colorFactory { 8 | constructor(name) { 9 | this.colors = {}; 10 | } 11 | create(name) { 12 | let color = this.colors[name]; 13 | if (color) return color; 14 | this.colors[name] = new Color(name); 15 | return this.colors[name]; 16 | } 17 | }; 18 | 19 | export { 20 | colorFactory 21 | }; 22 | -------------------------------------------------------------------------------- /src/structural/proxy/proxy.js: -------------------------------------------------------------------------------- 1 | function Car() { 2 | this.drive = function() { 3 | return "driving"; 4 | }; 5 | } 6 | 7 | function CarProxy(driver) { 8 | this.driver = driver; 9 | this.drive = function() { 10 | if (driver.age < 18) 11 | return "too young to drive"; 12 | return new Car().drive(); 13 | }; 14 | } 15 | 16 | function Driver(age) { 17 | this.age = age; 18 | } 19 | 20 | module.exports = [Car, CarProxy, Driver]; 21 | -------------------------------------------------------------------------------- /src/structural/proxy/proxy_es6.js: -------------------------------------------------------------------------------- 1 | class Car { 2 | drive() { 3 | return "driving"; 4 | }; 5 | } 6 | 7 | class CarProxy { 8 | constructor(driver) { 9 | this.driver = driver; 10 | } 11 | drive() { 12 | return (this.driver.age < 18) ? "too young to drive" : new Car().drive(); 13 | }; 14 | } 15 | 16 | class Driver { 17 | constructor(age) { 18 | this.age = age; 19 | } 20 | } 21 | 22 | export { 23 | Car, 24 | CarProxy, 25 | Driver 26 | }; 27 | -------------------------------------------------------------------------------- /test/abstract-factory-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const droidProducer = require('../src/creational/abstract-factory/abstract-factory'); 3 | import droidProducer6 from '../src/creational/abstract-factory/abstract-factory_es6'; 4 | 5 | describe('abstract factory test', () => { 6 | it('Battle droid', () => { 7 | const battleDroid = droidProducer('battle')(); 8 | expect(battleDroid.info()).to.equal('B1, Battle Droid'); 9 | }); 10 | 11 | it('pilot droid', () => { 12 | const pilotDroid = droidProducer('pilot')(); 13 | expect(pilotDroid.info()).to.equal('Rx24, Pilot Droid'); 14 | }); 15 | 16 | it('Battle droid es6', () => { 17 | const battleDroid = droidProducer6('battle')(); 18 | expect(battleDroid.info()).to.equal('B1, Battle Droid'); 19 | }); 20 | 21 | it('pilot droid 6', () => { 22 | const pilotDroid = droidProducer6('pilot')(); 23 | expect(pilotDroid.info()).to.equal('Rx24, Pilot Droid'); 24 | }); 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /test/adapter-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [Soldier, Jedi, JediAdapter] = require('../src/structural/adapter/adapter.js'); 4 | 5 | describe('adapter tests', () => { 6 | 7 | it('sanity', () => { 8 | var stormtrooper = new Soldier(1); 9 | var yoda = new JediAdapter(new Jedi(10)); 10 | expect(yoda.attack()).to.equal(stormtrooper.attack() * 1000); 11 | }); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /test/adapter_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | Soldier, 5 | Jedi, 6 | JediAdapter 7 | } from '../src/structural/adapter/adapter_es6'; 8 | 9 | describe('adapter_es6 tests', () => { 10 | 11 | it('sanity', () => { 12 | const stormtrooper = new Soldier(1); 13 | const yoda = new JediAdapter(new Jedi(10)); 14 | expect(yoda.attack()).to.equal(stormtrooper.attack() * 1000); 15 | }); 16 | 17 | }); 18 | -------------------------------------------------------------------------------- /test/bridge-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [EpsonPrinter, HPprinter, acrylicInk, alcoholInk] = require('../src/structural/bridge/bridge'); 4 | 5 | describe('bridge tests', () => { 6 | 7 | it('Epson test', () => { 8 | const printer = new EpsonPrinter(alcoholInk); 9 | const result = printer.print(); 10 | expect(result).to.equal("Printer: Epson, Ink: alcohol-based"); 11 | }); 12 | 13 | it('HP test', () => { 14 | const printer = new HPprinter(acrylicInk); 15 | const result = printer.print(); 16 | expect(result).to.equal("Printer: HP, Ink: acrylic-based"); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/bridge_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | EpsonPrinter, 5 | HPprinter, 6 | AcrylicInk, 7 | AlcoholInk 8 | } from '../src/structural/bridge/bridge_es6'; 9 | 10 | describe('bridge es6 tests', () => { 11 | 12 | it('Epson test', () => { 13 | const printer = new EpsonPrinter(new AlcoholInk()); 14 | const result = printer.print(); 15 | expect(result).to.equal("Printer: Epson, Ink: alcohol-based"); 16 | }); 17 | 18 | it('HP test', () => { 19 | const printer = new HPprinter(new AcrylicInk()); 20 | const result = printer.print(); 21 | expect(result).to.equal("Printer: HP, Ink: acrylic-based"); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/builder-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const RequestBuilder = require('../src/creational/builder/builder'); 3 | 4 | describe('builder test', () => { 5 | it('sanity', () => { 6 | var requestBuilder = new RequestBuilder(); 7 | var request = requestBuilder 8 | .forUrl('http://something/users') 9 | .useMethod('GET') 10 | .payload(null) 11 | .build(); 12 | 13 | expect(request.method).to.equal('GET'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/builder_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | import RequestBuilder from '../src/creational/builder/builder_es6'; 3 | 4 | describe('builder es6 test', () => { 5 | it('sanity', () => { 6 | const requestBuilder = new RequestBuilder(); 7 | const url = 'http://something/users'; 8 | const method = 'GET'; 9 | const request = requestBuilder 10 | .forUrl(url) 11 | .useMethod(method) 12 | .payload(null) 13 | .build(); 14 | 15 | expect(request.method).to.equal(method); 16 | expect(request.payload).to.be.null; 17 | expect(request.url).to.equal(url); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/chain-of-resp-es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | ShoppingCart, 5 | Discount 6 | } from '../src/behavioral/chain-of-resp/chain-of-resp-es6'; 7 | 8 | describe('chain of resp es6 tests', () => { 9 | 10 | it(' > $ 500', () => { 11 | const discount = new Discount(); 12 | 13 | const sc = new ShoppingCart(); 14 | sc.addProduct(1000); 15 | 16 | let resp = discount.calc(sc.products); 17 | 18 | expect(resp).to.equal(0.1); 19 | }); 20 | 21 | it('more than 3 products', () => { 22 | const discount = new Discount(); 23 | 24 | const sc = new ShoppingCart(); 25 | sc.addProduct(100); 26 | sc.addProduct(100); 27 | sc.addProduct(100); 28 | sc.addProduct(100); 29 | 30 | let resp = discount.calc(sc.products); 31 | 32 | expect(resp).to.equal(0.05); 33 | }); 34 | 35 | it('more than 3 products and > $ 500 ', () => { 36 | let discount = new Discount(); 37 | 38 | let sc = new ShoppingCart(); 39 | sc.addProduct(1000); 40 | sc.addProduct(100); 41 | sc.addProduct(100); 42 | sc.addProduct(100); 43 | 44 | let resp = discount.calc(sc.products); 45 | 46 | expect(resp.toFixed(2)).to.equal('0.15'); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/chain-of-resp-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [ShoppingCart, Discount] = require('../src/behavioral/chain-of-resp/chain-of-resp'); 4 | 5 | describe('chain of resp tests', () => { 6 | 7 | it(' > $ 500', () => { 8 | var discount = new Discount(); 9 | 10 | var sc = new ShoppingCart(); 11 | sc.addProduct(1000); 12 | 13 | var resp = discount.calc(sc.products); 14 | 15 | expect(resp).to.equal(0.1); 16 | }); 17 | 18 | it('more than 3 products', () => { 19 | var discount = new Discount(); 20 | 21 | var sc = new ShoppingCart(); 22 | sc.addProduct(100); 23 | sc.addProduct(100); 24 | sc.addProduct(100); 25 | sc.addProduct(100); 26 | 27 | var resp = discount.calc(sc.products); 28 | 29 | expect(resp).to.equal(0.05); 30 | }); 31 | 32 | it('more than 3 products and > $ 500 ', () => { 33 | var discount = new Discount(); 34 | 35 | var sc = new ShoppingCart(); 36 | sc.addProduct(1000); 37 | sc.addProduct(100); 38 | sc.addProduct(100); 39 | sc.addProduct(100); 40 | 41 | var resp = discount.calc(sc.products); 42 | 43 | expect(resp.toFixed(2)).to.equal('0.15'); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/command-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [Cockpit, Turbine, OnCommand, OffCommand, SpeedUpCommand, SpeedDownCommand] = require('../src/behavioral/command/command.js'); 4 | 5 | describe('command tests', () => { 6 | 7 | it('turn off/on test', () => { 8 | var turbine = new Turbine(); 9 | const onCommand = new OnCommand(turbine); 10 | const cockpit = new Cockpit(onCommand); 11 | cockpit.execute(); 12 | expect(turbine.state).to.be.true; 13 | }); 14 | 15 | it('speed test', () => { 16 | var turbine = new Turbine(); 17 | const onCommand = new OnCommand(turbine); 18 | var cockpit = new Cockpit(onCommand); 19 | cockpit.execute(); 20 | 21 | const speedUp = new SpeedUpCommand(turbine); 22 | cockpit = new Cockpit(speedUp); 23 | cockpit.execute(); 24 | 25 | expect(turbine.speed).to.equal(200); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/command_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | Cockpit, 5 | Turbine, 6 | OnCommand, 7 | OffCommand 8 | } from '../src/behavioral/command/command_es6'; 9 | 10 | describe('command es6 tests', () => { 11 | 12 | it('turn off/on test', () => { 13 | var turbine = new Turbine(); 14 | const onCommand = new OnCommand(turbine); 15 | const cockpit = new Cockpit(onCommand); 16 | cockpit.execute(); 17 | expect(turbine.state).to.be.true; 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/composite-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [EquipmentComposition, FloppyDisk, HardDrive, Memory] = require('../src/structural/composite/composite.js'); 4 | 5 | describe('composity tests', () => { 6 | 7 | it('sanity test', () => { 8 | var cabinet = new EquipmentComposition("cabinet"); 9 | cabinet.add(new FloppyDisk()); 10 | cabinet.add(new HardDrive()); 11 | cabinet.add(new Memory()); 12 | 13 | expect(cabinet.getPrice()).to.equal(600); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/composite_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | Cabinet, 5 | FloppyDisk, 6 | HardDrive, 7 | Memory 8 | } from '../src/structural/composite/composite_es6'; 9 | 10 | describe('composity tests', () => { 11 | 12 | it('sanity test', () => { 13 | const cabinet = new Cabinet(); 14 | cabinet.add(new FloppyDisk()); 15 | cabinet.add(new HardDrive()); 16 | cabinet.add(new Memory()); 17 | 18 | expect(cabinet.getPrice()).to.equal(600); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/decorator-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const [Penne, SauceDecorator, CheeseDecorator] = require('../src/structural/decorator/decorator'); 3 | 4 | describe('decorator tests', () => { 5 | 6 | it('sanity test', () => { 7 | var penne = new Penne(); 8 | var penneWithSauce = new SauceDecorator(penne); 9 | var penneWithSauceAndCheese = new CheeseDecorator(penneWithSauce); 10 | 11 | expect(penne.getPrice()).to.equal(8); 12 | expect(penneWithSauce.getPrice()).to.equal(13); 13 | expect(penneWithSauceAndCheese.getPrice()).to.equal(16); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/decorator_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | import { 3 | Penne, 4 | SauceDecorator, 5 | CheeseDecorator 6 | } from '../src/structural/decorator/decorator_es6'; 7 | 8 | describe('decorator es6 tests', () => { 9 | 10 | it('sanity test', () => { 11 | const penne = new Penne(); 12 | const penneWithSauce = new SauceDecorator(penne); 13 | const penneWithSauceAndCheese = new CheeseDecorator(penneWithSauce); 14 | 15 | expect(penne.getPrice()).to.equal(8); 16 | expect(penneWithSauce.getPrice()).to.equal(13); 17 | expect(penneWithSauceAndCheese.getPrice()).to.equal(16); 18 | }); 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /test/facade-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const shopFacade = require('../src/structural/facade/facade'); 4 | 5 | describe('facade tests', () => { 6 | 7 | it('sanity', () => { 8 | var result = shopFacade.calc(100); 9 | expect(result).to.equal(99.5); 10 | }); 11 | 12 | }); 13 | -------------------------------------------------------------------------------- /test/facade_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import ShopFacade from '../src/structural/facade/facade_es6'; 4 | 5 | describe('facade tests', () => { 6 | 7 | it('sanity', () => { 8 | const shop = new ShopFacade(); 9 | const result = shop.calc(100); 10 | expect(result).to.equal(99.5); 11 | }); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /test/factory-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const bmwFactory = require('../src/creational/factory/factory'); 3 | 4 | describe('factory test', () => { 5 | it('sanity', () => { 6 | var x5 = bmwFactory('X5'); 7 | var x6 = bmwFactory('X6'); 8 | 9 | expect(x5.price).to.equal(108000); 10 | expect(x6.price).to.equal(111000); 11 | expect(x5.maxSpeed).to.equal(300); 12 | expect(x6.maxSpeed).to.equal(320); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/factory_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | import BmwFactory from '../src/creational/factory/factory_es6'; 3 | 4 | describe('Factory es6 test', () => { 5 | it('We can create a X5 instance', () => { 6 | const x5 = BmwFactory.create('X5'); 7 | expect(x5.model).to.equal('X5'); 8 | }); 9 | 10 | it('The X5 price is properly set', () => { 11 | const x5 = BmwFactory.create('X5'); 12 | expect(x5.price).to.equal(108000); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/flyweight-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const colorFactory = require('../src/structural/flyweight/flyweight'); 4 | 5 | describe('flyweight tests', () => { 6 | 7 | it('sanity', () => { 8 | colorFactory.create('RED'); 9 | colorFactory.create('RED'); 10 | colorFactory.create('RED'); 11 | colorFactory.create('YELLOW'); 12 | colorFactory.create('YELLOW'); 13 | expect(Object.keys(colorFactory.colors)).to.have.lengthOf(2); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/flyweight_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | colorFactory 5 | } from '../src/structural/flyweight/flyweight_es6'; 6 | 7 | describe('flyweight tests', () => { 8 | 9 | it('sanity', () => { 10 | const cf = new colorFactory(); 11 | cf.create('RED'); 12 | cf.create('RED'); 13 | cf.create('RED'); 14 | cf.create('YELLOW'); 15 | cf.create('YELLOW'); 16 | expect(Object.keys(cf.colors)).to.have.lengthOf(2); 17 | }); 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /test/interpreter-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [Num, Min, Sum] = require('../src/behavioral/interpreter/interpreter'); 4 | 5 | describe('interpreter tests', () => { 6 | 7 | it('sanity', () => { 8 | var result = new Sum(new Num(100), new Min(new Num(100), new Num(50))); 9 | expect(result.interpret()).to.equal(150); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/interpreter_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | Num, 5 | Min, 6 | Sum 7 | } from '../src/behavioral/interpreter/interpreter_es6'; 8 | 9 | describe('interpreter tests', () => { 10 | 11 | it('sanity', () => { 12 | var result = new Sum(new Num(100), new Min(new Num(100), new Num(50))); 13 | expect(result.interpret()).to.equal(150); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/iterator-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const Iterator = require('../src/behavioral/iterator/iterator'); 4 | 5 | import Iterator6 from '../src/behavioral/iterator/iterator_es6'; 6 | 7 | describe('iterator tests', () => { 8 | 9 | it('sanity', () => { 10 | test(Iterator); 11 | }); 12 | 13 | }); 14 | 15 | describe('iterator es6 tests', () => { 16 | 17 | it('sanity', () => { 18 | test(Iterator6); 19 | }); 20 | 21 | }); 22 | 23 | function test(Iterator) { 24 | var numbers = new Iterator([1, 2, 3]); 25 | 26 | expect(numbers.next()).to.equal(1); 27 | expect(numbers.next()).to.equal(2); 28 | expect(numbers.next()).to.equal(3); 29 | expect(numbers.hasNext()).to.false; 30 | } 31 | -------------------------------------------------------------------------------- /test/mediator-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [TrafficTower, Airplane] = require('../src/behavioral/mediator/mediator'); 4 | 5 | describe('mediator tests', () => { 6 | 7 | it('sanity', () => { 8 | test(TrafficTower, Airplane); 9 | }); 10 | 11 | }); 12 | 13 | function test(TrafficTower, AirPlane) { 14 | var trafficTower = new TrafficTower(); 15 | var boeing1 = new Airplane(10, trafficTower); 16 | var boeing2 = new Airplane(15, trafficTower); 17 | var boeing3 = new Airplane(55, trafficTower); 18 | 19 | expect(boeing1.requestPositions()).to.deep.equals([10, 15, 55]); 20 | } 21 | -------------------------------------------------------------------------------- /test/mediator_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | TrafficTower, 5 | Airplane 6 | } from '../src/behavioral/mediator/mediator_es6'; 7 | 8 | describe('mediator es6 tests', () => { 9 | 10 | it('sanity', () => { 11 | const trafficTower = new TrafficTower(); 12 | const boeing1 = new Airplane(10, trafficTower); 13 | const boeing2 = new Airplane(15, trafficTower); 14 | const boeing3 = new Airplane(55, trafficTower); 15 | 16 | expect(boeing1.requestPositions()).to.deep.equals([10, 15, 55]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/memento-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [originator, Caretaker] = require('../src/behavioral/memento/memento'); 4 | 5 | describe('memento tests', () => { 6 | 7 | it('sanity', () => { 8 | var careTaker = new Caretaker(); 9 | careTaker.addMemento(originator.store("hello")); 10 | careTaker.addMemento(originator.store("hello world")); 11 | careTaker.addMemento(originator.store("hello world !!!")); 12 | 13 | var result = originator.restore(careTaker.getMemento(1)); 14 | expect(result).to.equal("hello world"); 15 | }); 16 | 17 | }); 18 | -------------------------------------------------------------------------------- /test/memento_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | originator, 5 | Caretaker 6 | } from '../src/behavioral/memento/memento_es6'; 7 | 8 | describe('memento es6 tests', () => { 9 | 10 | it('sanity', () => { 11 | const careTaker = new Caretaker(); 12 | careTaker.addMemento(originator.store("hello")); 13 | careTaker.addMemento(originator.store("hello world")); 14 | careTaker.addMemento(originator.store("hello world !!!")); 15 | 16 | var result = originator.restore(careTaker.getMemento(1)); 17 | expect(result).to.equal("hello world"); 18 | }); 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /test/observer-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const [Product, fees, proft] = require('../src/behavioral/observer/observer.js'); 3 | 4 | function register(p, f, t) { 5 | p.register(f); 6 | p.register(t); 7 | return p; 8 | } 9 | 10 | describe('Observer test', () => { 11 | 12 | it('Subscribers are triggered', () => { 13 | let product = register(new Product(), fees, proft); 14 | product.setBasePrice(100); 15 | expect(product.price).to.equal(240); 16 | }); 17 | 18 | it('We are able to unregister a subscriber', () => { 19 | let product = register(new Product(), fees, proft); 20 | product.unregister(proft); 21 | 22 | product.setBasePrice(100); 23 | expect(product.price).to.equal(120) 24 | }) 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /test/observer_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | import { 3 | Product, 4 | Fees, 5 | Proft 6 | } from '../src/behavioral/observer/observer_es6'; 7 | 8 | function register(p, f, t) { 9 | p.register(f); 10 | p.register(t); 11 | return p; 12 | } 13 | 14 | describe('Observer es6 test', () => { 15 | 16 | it('Subscribers are triggered', () => { 17 | let product = register(new Product(), new Fees(), new Proft()); 18 | product.setBasePrice(100); 19 | expect(product.price).to.equal(240); 20 | }); 21 | 22 | it('We are able to unregister a subscriber', () => { 23 | let product = register(new Product(), new Fees(), new Proft()); 24 | product.unregister(Proft); 25 | 26 | product.setBasePrice(100); 27 | expect(product.price).to.equal(120) 28 | }) 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /test/prototype-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const Sheep = require('../src/creational/prototype/prototype'); 3 | 4 | describe('prototype test', () => { 5 | it('sanity', () => { 6 | var sheep = new Sheep('dolly', 10.3); 7 | var dolly = sheep.clone(); 8 | expect(dolly.name).to.equal('dolly'); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/prototype_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | import Sheep from '../src/creational/prototype/prototype_es6'; 3 | 4 | describe('prototype_es6 test', () => { 5 | it('sanity', () => { 6 | var sheep = new Sheep('dolly', 10.3); 7 | var dolly = sheep.clone(); 8 | expect(dolly.name).to.equal('dolly'); 9 | expect(dolly.weight).to.equal(10.3); 10 | expect(dolly).to.be.instanceOf(Sheep); 11 | expect(dolly === sheep).to.be.false; 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/proxy-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [Car, CarProxy, Driver] = require('../src/structural/proxy/proxy'); 4 | 5 | describe('proxy tests', () => { 6 | 7 | it('sanity', () => { 8 | var driver = new Driver(28); 9 | var kid = new Driver(10); 10 | 11 | var car = new CarProxy(driver); 12 | expect(car.drive()).to.equal('driving'); 13 | 14 | car = new CarProxy(kid); 15 | expect(car.drive()).to.equal('too young to drive'); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /test/proxy_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | Car, 5 | CarProxy, 6 | Driver 7 | } from '../src/structural/proxy/proxy_es6'; 8 | 9 | describe('proxy es6 tests', () => { 10 | 11 | it('sanity', () => { 12 | let driver = new Driver(28); 13 | let kid = new Driver(10); 14 | 15 | let car = new CarProxy(driver); 16 | expect(car.drive()).to.equal('driving'); 17 | 18 | car = new CarProxy(kid); 19 | expect(car.drive()).to.equal('too young to drive'); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /test/singleton-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const Person = require('../src/creational/singleton/singleton'); 3 | 4 | describe('singleton test', () => { 5 | it('sanity', () => { 6 | var john = new Person(); 7 | var john2 = new Person(); 8 | 9 | expect(john).to.equal(john2); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/singleton_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | import Person from '../src/creational/singleton/singleton_es6'; 3 | 4 | describe('singleton_es6 test', () => { 5 | it('sanity', () => { 6 | var john = new Person(); 7 | var john2 = new Person(); 8 | 9 | expect(john).to.equal(john2); 10 | expect(john === john2).to.be.true; 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /test/state-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const Order = require('../src/behavioral/state/state'); 4 | 5 | describe('state tests', () => { 6 | 7 | it('sanity', () => { 8 | var order = new Order(); 9 | expect(order.state.name).to.equal('waitingForPayment'); 10 | order.nextState(); 11 | expect(order.state.name).to.equal('shipping'); 12 | order.nextState(); 13 | expect(order.state.name).to.equal('delivered'); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/state_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import Order from '../src/behavioral/state/state_es6'; 4 | 5 | describe('state_es6 tests', () => { 6 | 7 | it('sanity', () => { 8 | const order = new Order(); 9 | expect(order.state.name).to.equal('waitingForPayment'); 10 | order.nextState(); 11 | expect(order.state.name).to.equal('shipping'); 12 | order.nextState(); 13 | expect(order.state.name).to.equal('delivered'); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/strategy-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [ShoppingCart, guestStrategy, regularStrategy, premiumStrategy] = require('../src/behavioral/strategy/strategy.js'); 4 | 5 | describe('strategy tests', () => { 6 | 7 | it('guest test', () => { 8 | var guestCart = new ShoppingCart(guestStrategy); 9 | guestCart.setAmount(100); 10 | expect(guestCart.checkout()).to.equal(100); 11 | }); 12 | 13 | it('regular test', () => { 14 | var regularCart = new ShoppingCart(regularStrategy); 15 | regularCart.setAmount(100); 16 | expect(regularCart.checkout()).to.equal(90); 17 | }); 18 | 19 | it('premium test', () => { 20 | var premiumCart = new ShoppingCart(premiumStrategy); 21 | premiumCart.setAmount(100); 22 | expect(premiumCart.checkout()).to.equal(80); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/strategy_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | ShoppingCart, 5 | guestStrategy, 6 | regularStrategy, 7 | premiumStrategy 8 | } from '../src/behavioral/strategy/strategy_es6.js'; 9 | 10 | describe('strategy es6 tests', () => { 11 | 12 | it('guest test', () => { 13 | const guestCart = new ShoppingCart(guestStrategy); 14 | guestCart.setAmount(100); 15 | expect(guestCart.checkout()).to.equal(100); 16 | }); 17 | 18 | it('regular test', () => { 19 | const regularCart = new ShoppingCart(regularStrategy); 20 | regularCart.setAmount(100); 21 | expect(regularCart.checkout()).to.equal(90); 22 | }); 23 | 24 | it('premium test', () => { 25 | const premiumCart = new ShoppingCart(premiumStrategy); 26 | premiumCart.setAmount(100); 27 | expect(premiumCart.checkout()).to.equal(80); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/template-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [Tax1, Tax2] = require('../src/behavioral/template/template'); 4 | 5 | describe('template tests', () => { 6 | 7 | it('sanity', () => { 8 | var tax1 = new Tax1(); 9 | var tax2 = new Tax2(); 10 | 11 | expect(tax1.calc(1000)).to.equal(1110); 12 | expect(tax2.calc(1000)).to.equal(1210); 13 | expect(tax2.calc(100)).to.equal(110); 14 | }); 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /test/template_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | import { 4 | Tax1, 5 | Tax2 6 | } from '../src/behavioral/template/template_es6'; 7 | 8 | describe('template es6 tests', () => { 9 | 10 | it('sanity', () => { 11 | const tax1 = new Tax1(); 12 | const tax2 = new Tax2(); 13 | 14 | expect(tax1.calc(1000)).to.equal(1110); 15 | expect(tax2.calc(1000)).to.equal(1210); 16 | expect(tax2.calc(100)).to.equal(110); 17 | }); 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /test/visitor-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | 3 | const [Developer, Manager, bonusVisitor] = require('../src/behavioral/visitor/visitor.js'); 4 | 5 | describe('visitor tests', () => { 6 | it('sanity', () => { 7 | var employees = []; 8 | 9 | var john = new Developer(4000); 10 | var maria = new Developer(4000); 11 | var christian = new Manager(10000); 12 | 13 | employees.push(john); 14 | employees.push(maria); 15 | employees.push(christian); 16 | 17 | employees.forEach(e => { 18 | e.accept(bonusVisitor); 19 | }); 20 | 21 | expect(john.bonus).to.equal(4000); 22 | expect(christian.bonus).to.equal(20000); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/visitor_es6-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | import { 3 | Developer, 4 | Manager, 5 | bonusVisitor 6 | } from '../src/behavioral/visitor/visitor_es6'; 7 | 8 | describe('visitor es6 tests', () => { 9 | it('sanity', () => { 10 | let employees = []; 11 | 12 | const john = new Developer(4000); 13 | const maria = new Developer(4000); 14 | const christian = new Manager(10000); 15 | 16 | employees.push(john); 17 | employees.push(maria); 18 | employees.push(christian); 19 | 20 | employees.forEach(e => { 21 | e.accept(bonusVisitor); 22 | }); 23 | 24 | expect(john.bonus).to.equal(4000); 25 | expect(christian.bonus).to.equal(20000); 26 | }); 27 | }); 28 | --------------------------------------------------------------------------------