├── LICENSE ├── README.md ├── behavioral ├── chain-of-responsibility.js ├── command.js ├── mediator.js ├── observer.js ├── state.js ├── strategy.js ├── template-method.js └── visitor.js ├── creational ├── abstract-factory.js ├── builder.js ├── factory-method.js └── singleton.js └── structural ├── adaptor.js ├── composite.js ├── decorator.js ├── facade.js └── proxy.js /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2010 Nikolay V. Nemshilov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RightJS Design Patterns 2 | 3 | Here we have some software design patterns implemented using RightJS 4 | 5 | Have fun! 6 | -------------------------------------------------------------------------------- /behavioral/chain-of-responsibility.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Chain of responsibility 3 | * 4 | * Contains a list of command objects that process 5 | * an incomming object one by one. Allows you dinamically 6 | * reorganize the handlers and ensure that only one handler 7 | * have access to the object at a time. 8 | * 9 | * Copyright (C) 2010 Nikolay Nemshilov 10 | */ 11 | 12 | // defining the object that will be processed 13 | var Car = new Class({ 14 | initialize: function(options) { 15 | this.gas = options.gas; 16 | this.oil = options.oil; 17 | this.tires = options.tires; 18 | } 19 | }); 20 | 21 | // defining an abstract command 22 | var Check = new Class({ 23 | next: null, 24 | 25 | check: function(car) { 26 | if (this.next) { 27 | this.next.check(car); 28 | } 29 | } 30 | }); 31 | 32 | // defining the specific checks 33 | var GasCheck = new Class({ 34 | check: function(car) { 35 | if (car.gas < 100) 36 | console.log("Needs gas refill"); 37 | 38 | this.$super(car); 39 | } 40 | }); 41 | 42 | var OilCheck = new Class({ 43 | check: function(car) { 44 | if (car.oil < 100) 45 | console.log("Needs oil refill"); 46 | 47 | this.$super(car); 48 | } 49 | }); 50 | 51 | var TiresCheck = new Class({ 52 | check: function(car) { 53 | if (car.tires < 100) 54 | console.log("Needs some pressure in the tires"); 55 | 56 | this.$super(car); 57 | } 58 | }); 59 | 60 | // creating the chain holder object 61 | var PitStop = new Class({ 62 | initialize: function() { 63 | // creating the check points 64 | this.gasCheck = new GasCheck(); 65 | this.oilCheck = new OilCheck(); 66 | this.tiresCheck = new TiresCheck(); 67 | 68 | // joining them together 69 | this.firstCheck = this.gasCheck; 70 | this.firstCheck.next = this.oilCheck; 71 | this.oilCheck.next = this.tiresCheck; 72 | }, 73 | 74 | check: function(car) { 75 | return this.firstCheck.check(car); 76 | } 77 | }); 78 | 79 | // application 80 | var used_car = new Car({ 81 | gas: 40, 82 | oil: 40, 83 | tires: 40 84 | }); 85 | 86 | var pit_stop = new PitStop(); 87 | 88 | // will run the car through gas, oil and tires check 89 | pit_stop.check(used_car); -------------------------------------------------------------------------------- /behavioral/command.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Command 3 | * 4 | * Encapsulates an object and everything that needed to call 5 | * it inside a command object, representing it as an independent 6 | * unit that knows how to do some action. 7 | * 8 | * Oftenly used to make undoable commands 9 | * 10 | * Copyright (C) 2010 Nikolay Nemshilov 11 | */ 12 | 13 | 14 | // here we have two dummy units 15 | var Light = new Class({ 16 | turnOn: function() { 17 | return "The light is on"; 18 | }, 19 | 20 | turnOff: function() { 21 | return "The light is off"; 22 | } 23 | }); 24 | 25 | var Music = new Class({ 26 | play: function() { 27 | return "The music is on"; 28 | }, 29 | 30 | stop: function() { 31 | return "The music is off"; 32 | } 33 | }); 34 | 35 | // and a switch wich can be used with any of those two 36 | var Switch = new Class({ 37 | initialize: function(command) { 38 | this.command = command; 39 | }, 40 | 41 | flipUp: function() { 42 | return this.command.execute(); 43 | }, 44 | 45 | flipDown: function() { 46 | return this.command.undo(); 47 | } 48 | }); 49 | 50 | // an abstract command interface 51 | var Command = new Class({ 52 | initialize: function(object) { 53 | this.object = object; 54 | }, 55 | 56 | execute: function() { 57 | throw "Abstract method"; 58 | }, 59 | 60 | undo: function() { 61 | throw "Abstract method"; 62 | } 63 | }); 64 | 65 | // now we have two commands, one turns on lights and another turns on the music 66 | var LightOnCommand = new Class(Command, { 67 | execute: function() { 68 | return this.object.turnOn(); 69 | }, 70 | 71 | undo: function() { 72 | return this.object.turnOff(); 73 | } 74 | }); 75 | 76 | var MusicOnCommand = new Class(Command, { 77 | execute: function() { 78 | return this.object.play(); 79 | }, 80 | 81 | undo: function() { 82 | return this.object.stop(); 83 | } 84 | }); 85 | 86 | 87 | // putting everything together 88 | var lamp = new Light(); 89 | var player = new Music(); 90 | 91 | var lamp_on_command = new LightOnCommand(lamp); 92 | var player_on_command = new MusicOnCommand(player); 93 | 94 | var lamp_switch = new Switch(lamp_on_command); 95 | var player_switch = new Switch(player_on_command); 96 | 97 | lamp_switch.flipUp(); // -> 'The light is on' 98 | lamp_switch.flipDown(); // -> 'The light is off' 99 | 100 | player_switch.flipUp(); // -> 'The music is on' 101 | player_switch.flipDown(); // -> 'The music is off' 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /behavioral/mediator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mediator 3 | * 4 | * Incapsulates the logic of interaction between several objects. 5 | * Promotes loose coupling by keeping the objects from referring 6 | * each other directly 7 | * 8 | * Copyright (C) 2010 Nikolay Nemshilov 9 | */ 10 | 11 | var Dialog = new Class({ 12 | initialize: function(element) { 13 | this.label = element.first('label'); 14 | this.createButton = element.first('button.create'); 15 | this.previewButton = element.first('button.preview'); 16 | }, 17 | 18 | createMode: function() { 19 | this.label = "Create"; 20 | this.createButton.hide(); 21 | this.previewButton.show(); 22 | }, 23 | 24 | previewMode: function() { 25 | this.label = "Preview"; 26 | this.createButton.show(); 27 | this.previewButton.hide(); 28 | } 29 | }); -------------------------------------------------------------------------------- /behavioral/observer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Observer 3 | * 4 | * Creates an object to which some listeners can subscribe and 5 | * when the object changes it's state all the subscribed 6 | * listeners will be notified 7 | * 8 | * Copyright (C) 2010 Nikolay Nemshilov 9 | */ 10 | 11 | // RightJS has a built in `Observer` unit which already implements the pattern 12 | 13 | var MyObserver = new Class(Observer, { 14 | 15 | }); 16 | 17 | var my_observer = new MyObserver(); 18 | 19 | // attaching the listeners 20 | my_observer.on('event', function(value) { 21 | console.log("Listener 1", value); 22 | }); 23 | 24 | my_observer.on('event', function(value) { 25 | console.log("Listener 2", value); 26 | }); 27 | 28 | my_observer.on('event', function(value) { 29 | console.log("Listener 3", value); 30 | }); 31 | 32 | 33 | // firing the event 34 | my_observer.fire('event', 'some value'); 35 | 36 | // will log string like that 37 | // 'Listener 1', 'some value' 38 | // 'Listener 2', 'some value' 39 | // 'Listener 3', 'some value' 40 | 41 | // See the documentation on the official cite for more information on the unit -------------------------------------------------------------------------------- /behavioral/state.js: -------------------------------------------------------------------------------- 1 | /** 2 | * State 3 | * 4 | * Allows to alter the object's behavior depending on it's state 5 | * 6 | * Copyright (C) 2010 Nikolay Nemshilov 7 | */ 8 | 9 | // defining the states 10 | var RawState = new Class({ 11 | description: function() { 12 | return "Needs to be cooked"; 13 | } 14 | }); 15 | 16 | var CookedState = new Class({ 17 | description: function() { 18 | return "Ready to be eaten"; 19 | } 20 | }); 21 | 22 | // defining a context for the states 23 | var Food = new Class({ 24 | initialize: function() { 25 | this.state = new RawState(); 26 | }, 27 | 28 | check: function() { 29 | return this.state.description(); 30 | }, 31 | 32 | cook: function() { 33 | this.state = new CookedState(); 34 | } 35 | }); 36 | 37 | 38 | // application 39 | 40 | var steak = new Food(); 41 | 42 | steak.check(); // -> 'Needs to be cooked' 43 | 44 | steak.cook(); 45 | 46 | steak.check(); // -> 'Ready to be eaten' 47 | -------------------------------------------------------------------------------- /behavioral/strategy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Strategy 3 | * 4 | * Defines a family of algorithms with the same interface, then 5 | * incapsulates them in the some context making them interchangable 6 | * 7 | * Copyright (C) 2010 Nikolay Nemshilov 8 | */ 9 | 10 | // defining our strategies 11 | var W3C_Strategy = new Class({ 12 | operation: function() { 13 | return "Let's do that normal way"; 14 | } 15 | }); 16 | 17 | var IE_Strategy = new Class({ 18 | operation: function() { 19 | return "Okay, let's do it their way"; 20 | } 21 | }); 22 | 23 | // creating the context 24 | var StrategyContext = new Class({ 25 | strategy: null, 26 | 27 | initialize: function() { 28 | this.strategy = new (Browser.IE ? IE_Strategy : W3C_Strategy); 29 | }, 30 | 31 | operation: function() { 32 | return this.strategy.operation(); 33 | } 34 | }); 35 | 36 | 37 | // application 38 | var object = new StrategyContext(); 39 | 40 | // will return different results depending on the current browser 41 | object.operation(); -------------------------------------------------------------------------------- /behavioral/template-method.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Template method 3 | * 4 | * Defines the skeleton of an algorithm in an operation, deferring 5 | * some steps to subclasses. 6 | * 7 | * Copyright (C) 2010 Nikolay Nemshilov 8 | */ 9 | 10 | var Game = new Class({ 11 | players: null, 12 | result: null, 13 | 14 | run: function() { 15 | this.reset(); 16 | this.play(); 17 | this.print(); 18 | }, 19 | 20 | reset: function() { 21 | throw "Abstract method"; 22 | }, 23 | 24 | play: function() { 25 | throw "Abstract method"; 26 | }, 27 | 28 | print: function() { 29 | console.log(this.result); 30 | } 31 | }); 32 | 33 | 34 | var Chess = new Class(Game, { 35 | reset: function() { 36 | this.players = players: ['Kasparov', 'Computer']; 37 | this.result = null; 38 | }, 39 | 40 | play: function() { 41 | this.result = "Computer wins, humanity is doomed"; 42 | } 43 | }); 44 | 45 | 46 | var RussianRulette = new Class(Game, { 47 | reset: function() { 48 | this.players = [0, 0, 0, 0, 0, 1]; 49 | this.result = null; 50 | }, 51 | 52 | play: function() { 53 | this.result = this.players.random() ? 54 | "You're dead" : "You're lucky"; 55 | } 56 | }); -------------------------------------------------------------------------------- /behavioral/visitor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Visitor 3 | * 4 | * Separates a handling algorithm from an actual object, 5 | * so that you could create new algorithms without changing 6 | * the original object 7 | * 8 | * Copyright (C) 2010 Nikolay Nemshilov 9 | */ 10 | var Car = new Class({ 11 | initialize: function(options) { 12 | this.wheels = options.wheels; 13 | this.engine = options.engine; 14 | this.body = options.body; 15 | }, 16 | 17 | visitBy: function(visitor) { 18 | visitor.visit(this); 19 | }, 20 | 21 | show: function() { 22 | return "I have: " + 23 | this.wheels + " wheels, " + 24 | this.engine + " engine and " + 25 | this.body + " body"; 26 | } 27 | }); 28 | 29 | // definging some visitors 30 | var CarVisitor = new Class({ 31 | visit: function(car) { 32 | throw "Abstract method"; 33 | } 34 | }); 35 | 36 | var SportCarVisitor = new Class(CarVisitor, { 37 | visit: function(car) { 38 | car.wheels = 'sport'; 39 | car.engine = 'loud'; 40 | car.body = 'light'; 41 | } 42 | }); 43 | 44 | var GirlsCarVisitor = new Class(CarVisitor, { 45 | visit: function(car) { 46 | car.wheels = 'glossy'; 47 | car.engine = 'quiet'; 48 | car.body = 'pink'; 49 | } 50 | }); 51 | 52 | // application test 53 | var car = new Car({ 54 | wheels: 'stock', 55 | engine: 'stock', 56 | body: 'stock' 57 | }); 58 | 59 | var sport_car_mechanic = new SportCarVisitor(); 60 | var girls_car_mechanic = new GirlsCarVisitor(); 61 | 62 | car.visitBy(sport_car_mechanic); 63 | car.show(); // -> "I have: sport wheels, loud engine and light body" 64 | 65 | car.visitBy(girls_car_mechanic); 66 | car.show(); // -> "I have: glossy wheels, quied engine and pink body" 67 | -------------------------------------------------------------------------------- /creational/abstract-factory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Abstract factory 3 | * 4 | * Provides an interface for creating groups of related objects without 5 | * specifying their actual type 6 | * 7 | * Copyright (C) 2010 Nikolay Nemshilov 8 | */ 9 | 10 | // defining the abstractions 11 | var Alive = new Class({ 12 | grow: function() { 13 | throw "An abstract method"; 14 | } 15 | }); 16 | 17 | var Plant = new Class(Alive, { 18 | name: null, 19 | }); 20 | 21 | var Animal = new Class(Alive, { 22 | eat: function(plant) { 23 | throw "An abstract method"; 24 | } 25 | }); 26 | 27 | // defining the specializations 28 | var Grass = new Class(Plant, { 29 | name: "grass", 30 | 31 | grow: function() { 32 | return "The grass grows"; 33 | } 34 | }); 35 | 36 | var Horse = new Class(Animal, { 37 | grow: function() { 38 | return "The horsy grows"; 39 | }, 40 | 41 | eat: function(plant) { 42 | return "And now this horsy happily eats the "+ plant.name; 43 | } 44 | }); 45 | 46 | 47 | // abstract factory 48 | var AbstractFactory = new Class({ 49 | create_alive: function() { 50 | throw "An abstract method"; 51 | } 52 | }); 53 | 54 | 55 | // specific factories 56 | var Farm = new Class(AbstractFactory, { 57 | create_alive: function() { 58 | return new Grass(); 59 | } 60 | }); 61 | 62 | var Ranch = new Class(AbstractFactory, { 63 | create_alive: function() { 64 | return new Horse(); 65 | } 66 | }); 67 | 68 | 69 | // application 70 | var farm = new Farm(); 71 | var ranch = new Ranch(); 72 | 73 | var plant = farm.create_alive(); 74 | var animal = ranch.create_alive(); 75 | 76 | plant.grow(); // -> 'The grass grows' 77 | animal.grow(); // -> 'The horsy grows' 78 | animal.eat(plant); // -> 'And now this horsy happyliy eats the grass' 79 | 80 | -------------------------------------------------------------------------------- /creational/builder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Builder 3 | * 4 | * Separates construction of a complex object from its representation 5 | * so that the same construction process could build different things 6 | * 7 | * Copyright (C) 2010 Nikolay Nemshilov 8 | */ 9 | 10 | var WidgetBuilder = new Class({ 11 | build_part1: function() { 12 | return "part1"; 13 | }, 14 | 15 | build_part2: function() { 16 | return "part2"; 17 | }, 18 | 19 | build_part3: function() { 20 | return "part3"; 21 | } 22 | }); 23 | 24 | // creating the IE specific widget builder 25 | var IEWidgetBuilder = new Class(WidgetBuilder, { 26 | build_part1: function() { 27 | return this.$super() + " IE fixes"; 28 | } 29 | }); 30 | 31 | // the actual widget 32 | var Widget = new Class({ 33 | initialize: function() { 34 | this.builder = new (Browser.IE ? IEWidgetBuilder : WidgetBuilder); 35 | }, 36 | 37 | construct: function() { 38 | return [ 39 | this.builder.build_part1(), 40 | this.builder.build_part2(), 41 | this.builder.build_part3() 42 | ].join(", "); 43 | } 44 | }); 45 | 46 | 47 | var widget = new Widget(); 48 | 49 | widget.construct(); 50 | 51 | // Under W3C browser will return 52 | // 'part1, part2, part3' 53 | // 54 | // Under IE browser will return 55 | // 'part1 + IE fixes, part2, part3' 56 | -------------------------------------------------------------------------------- /creational/factory-method.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Factory method 3 | * 4 | * Defines an interface that creates instances of classes with certain 5 | * type and then delegates to subclasses to decide which exactly type 6 | * should be used to create the objects 7 | * 8 | * Copyright (C) 2010 Nikolay Nemshilov 9 | */ 10 | 11 | // the basic unit 12 | var Zing = new Class({ 13 | foo: function() { 14 | throw "An abstract method"; 15 | } 16 | }); 17 | 18 | // some versions of the Zings 19 | var RoundZing = new Class(Zing, { 20 | foo: function() { 21 | return "(Zing!)"; 22 | } 23 | }); 24 | 25 | var SquareZing = new Class(Zing, { 26 | foo: function() { 27 | return "[Zing!]"; 28 | } 29 | }); 30 | 31 | 32 | // the abstract zings creator 33 | var ZingsCreator = new Class({ 34 | create: function() { 35 | throw "An abstract method"; 36 | } 37 | }); 38 | 39 | 40 | // some versions of the zings creator 41 | var RoundZingsCreator = new Class(ZingsCreator, { 42 | create: function() { 43 | return new RoundZing(); 44 | } 45 | }); 46 | 47 | var SquareZingsCreator = new Class(ZingsCreator, { 48 | create: function() { 49 | return new SquareZing(); 50 | } 51 | }); 52 | 53 | // testing 54 | var round_zings = new RoundZingsCreator(); 55 | var square_zings = new SquareZingsCreator(); 56 | 57 | round_zings.create().foo(); // -> '(Zing!)' 58 | square_zings.create().foo(); // -> '[Zing!]' 59 | 60 | 61 | -------------------------------------------------------------------------------- /creational/singleton.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Singleton 3 | * 4 | * Ensures that you always have only one instance of your class 5 | * 6 | * Copyright (C) 2010 Nikolay Nemshilov 7 | */ 8 | 9 | // A classical singleton in RightJS would look like that 10 | var Klass = new Class({ 11 | extend: { 12 | instance: function() { 13 | return this._instance = this._instance || new this; 14 | }, 15 | _instance: null 16 | } 17 | }); 18 | 19 | var inst1 = Klass.instance(); 20 | inst1.someVariable = 'shared data'; 21 | 22 | var inst2 = Klass.instance(); 23 | inst2.someVariable; // -> 'shared data' 24 | 25 | 26 | /** 27 | * You also can implement singleton in a nicer way using the feature 28 | * of the `Class` unit, which allows you to return data from the 29 | * constructor 30 | */ 31 | var Klass = new Class({ 32 | extend: { 33 | instance: null 34 | }, 35 | 36 | initialize: function() { 37 | return Klass.instance = Klass.instance || this; 38 | } 39 | }); 40 | 41 | var inst1 = new Klass(); 42 | inst1.someVariable = 'shared data'; 43 | 44 | var inst2 = new Klass(); 45 | inst2.someVariable; // -> 'shared data'; 46 | 47 | -------------------------------------------------------------------------------- /structural/adaptor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adaptor 3 | * 4 | * Converts an interface of one class into another interface that 5 | * a client application expect 6 | * 7 | * Copyright (C) 2010 Nikolay Nemshilov 8 | */ 9 | 10 | var Kenobi = new Class({ 11 | skywalker_should_grow_first: function() { 12 | return "moaning..."; 13 | } 14 | }); 15 | 16 | var Yoda = new Class({ 17 | grow_first_skywalker_should: function() { 18 | return "moaning..."; 19 | } 20 | }); 21 | 22 | // now we can transform the Yoda interface 23 | Yoda.include({ 24 | skywalker_should_grow_first: function() { 25 | return this.grow_first_skywalker_should(); 26 | } 27 | }); 28 | 29 | // application 30 | var jedis = [ new Kenobi(), new Yoda() ]; 31 | 32 | jedis.each(function(jedi) { 33 | condole.log("Message", jedi.skywalker_should_grow_first()); 34 | }); 35 | -------------------------------------------------------------------------------- /structural/composite.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Compsite 3 | * 4 | * This is your classicall tree structures where leaves and 5 | * nodes are both inherited from the same super class so that 6 | * they had an uniformed interface 7 | * 8 | * Copyright (C) 2010 Nikolay Nemshilov 9 | */ 10 | var Item = new Class({ 11 | initialize: function(name) { 12 | this.name = name; 13 | }, 14 | 15 | getName: function() { 16 | return this.name; 17 | } 18 | }); 19 | 20 | var Leaf = new Class(Item, { 21 | 22 | }); 23 | 24 | var Node = new Class(Item, { 25 | initialize: function(name) { 26 | this.$super(name); 27 | this.children = []; 28 | }, 29 | 30 | add: function(item) { 31 | this.children.push(item); 32 | }, 33 | 34 | remove: function(item) { 35 | this.children = this.children.without(item); 36 | }, 37 | 38 | list: function() { 39 | return this.children; 40 | } 41 | }); 42 | 43 | 44 | // application 45 | 46 | var root = new Node('Root'); 47 | 48 | root.add(new Node('Dir 1')); 49 | root.add(new Node('Dir 2')); 50 | root.add(new Leaf('File 1')); 51 | root.add(new Leaf('File 2')); 52 | 53 | root.list().map('getName').join(', '); 54 | 55 | // will return 'Dir 1, Dir 2, File 1, File 2' 56 | -------------------------------------------------------------------------------- /structural/decorator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Decorator 3 | * 4 | * Attaches additional responsibilites to an object dynamically. 5 | * Provides an alternative to traditional inheritance approach 6 | * 7 | * Copyright (C) 2010 Nikolay Nemshilov 8 | */ 9 | 10 | var Zing = new Class({ 11 | foo: function() { 12 | console.log("Zing#foo"); 13 | } 14 | }); 15 | 16 | 17 | // traditional approach with inheritance 18 | var ZingSuccessor = new Class(Zing, { 19 | pre_foo: function() { 20 | console.log("Pre #foo call"); 21 | }, 22 | 23 | foo: function() { 24 | this.pre_foo(); 25 | 26 | this.$super(); 27 | } 28 | }); 29 | 30 | 31 | // decorator approach 32 | var ZingDecorator = new Class(Zing, { 33 | initialize: function(zing) { 34 | this.zing = zing; 35 | }, 36 | 37 | pre_foo: function() { 38 | console.log("Pre #foo call"); 39 | }, 40 | 41 | foo: function() { 42 | this.pre_foo(); 43 | 44 | this.zing.foo(); 45 | } 46 | }); 47 | 48 | 49 | // application 50 | 51 | var zings = [ 52 | new Zing(), 53 | new ZingSuccessor(), 54 | new ZingDecorator() 55 | ]; 56 | 57 | zings.each('foo'); 58 | -------------------------------------------------------------------------------- /structural/facade.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Facade 3 | * 4 | * Provide a unified interface to a set of interfaces in a subsystem. 5 | * Facade defines a higher-level interface that makes the subsystem easier to use. 6 | * 7 | * Copyright (C) 2010 Nikolay Nemshilov 8 | */ 9 | 10 | var Item1 = new Class({ 11 | operation_1: function() { 12 | console.log("Item1#operation1"); 13 | } 14 | }); 15 | 16 | var Item2 = new Class({ 17 | operation_2: function() { 18 | console.log("Item2#operation2"); 19 | } 20 | }); 21 | 22 | var Item3 = new Class({ 23 | operation_3: function() { 24 | console.log("Item3#operation3"); 25 | } 26 | }); 27 | 28 | 29 | // the facade interface 30 | var Facade = new Class({ 31 | initialize: function() { 32 | this.item_1 = new Item1(); 33 | this.item_2 = new Item2(); 34 | this.item_3 = new Item3(); 35 | }, 36 | 37 | operate: function() { 38 | this.item_1.operation_1(); 39 | this.item_2.operation_2(); 40 | this.item_3.operation_3(); 41 | } 42 | }); 43 | 44 | 45 | // test 46 | new Facade().operate(); -------------------------------------------------------------------------------- /structural/proxy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Proxy 3 | * 4 | * Provides a surrogate for another object to access control to it 5 | * 6 | * Copyright (C) 2010 Nikolay Nemshilov 7 | */ 8 | 9 | var Dialog = new Class({ 10 | 11 | initialize: function(element) { 12 | this.element = element; 13 | }, 14 | 15 | close: function() { 16 | this.element.hide(); 17 | } 18 | 19 | }); 20 | 21 | var DialogProxy = new Class(Dialog, { 22 | close: function() { 23 | if (confirm("Are you sure?")) 24 | this.$super(); 25 | } 26 | }); 27 | --------------------------------------------------------------------------------