js design patterns & reactive programming

26
JS DESIGN PATTERNS & REACTIVE PROGRAMMING

Upload: adrian-badarau

Post on 22-Jan-2018

394 views

Category:

Technology


4 download

TRANSCRIPT

JS DESIGN PATTERNS

& REACTIVE PROGRAMMING

The implementation of a design pattern per day

keeps the angry developer away

-Old Chinese proverb

WHY USE ?

➤ easy way to refer to a very specific implementation of code

➤ can help to thwart minor issues that can create major problems later down the road

➤ Patterns exemplify a level of sophistication that can provide both performance boosts

and reduction of excess memory usage

➤ Patterns established through rigorous testing which means they are, for the most

part, "tried and true" and do aid in solving a particular type of problem

DESIGN PATTERN CATEGORIES

This category deals directly with object initialisation procedures focusing on the creation of

situation-specific objects.

It works to solve the problem of how objects are created by adding a layer to this process

CREATIONAL DESIGN PATTERNS

image credit: https://addyosmani.com/resources/essentialjsdesignpatterns/book/#categoriesofdesignpatterns

SINGLETON

➤ Advantages

➤ Reduced memory footprint

➤ Single point of access

➤ Delayed initialisation that prevents

instantiation until required

➤ Disadvantages

➤ Once instantiated, they're hardly ever “reset"

➤ Harder to unit test and sometimes introduces

hidden dependencies

var mySingleton = ( function( window, undefined ) {

var instance = null;

// revealing module pattern that handles initialization of our new module

function initializeNewModule() {

function myMethod() {

alert( 'my method' );

}

function myOtherMethod() {

alert( 'my other method' );

}

return {

someMethod : myMethod,

someOtherMethod : myOtherMethod

};

}

// handles the prevention of additional instantiations

function getInstance() {

if( ! instance ) {

instance = new initializeNewModule();

}

return instance;

}

return {

getInstance : getInstance

};

} )( window );

// example usage

mySingleton.getInstance().someMethod(); // alerts "my method"

mySingleton.getInstance().someOtherMethod(); // alerts "my other method"

FACTORY

➤ Advantages

➤ Makes complex object creation easy through an

interface that can bootstrap this process for you

➤ Great for generating different objects based on

the environment

➤ Practical for components that require similar

instantiation or methods

➤ Great for decoupling components by

bootstrapping the instantiation of a different

object to carry out work for particular instances

➤ Disadvantages

➤ Unit testing can be difficult as a direct

result of the object creation process being

hidden by the factory methods

function CarDoor( options ) {

this.color = options.color || 'red';

this.side = options.side || 'right';

this.hasPowerWindows = options.hasPowerWindows || true;

}

function CarSeat( options ) {

this.color = options.color || 'gray';

this.material = options.material || 'leather';

this.isReclinable = options.isReclinable || true;

}

function CarPartFactory() {}

CarPartFactory.prototype.createPart = function createCarPart( options ) {

var parentClass = null;

if( options.partType === 'door' ) {

parentClass = CarDoor;

} else if( options.partType === 'seat' ) {

parentClass = CarSeat;

}

if( parentClass === null ) {

return false;

}

return new parentClass( options );

}

// example usage

var myPartFactory = new CarPartFactory();

var seat = myPartFactory.createPart( {

partType : 'seat',

material : 'leather',

color : 'blue',

isReclinable : false

} );

// outputs: true

console.log( seat instanceof CarSeat );

// outputs a CarSeat object with material "leather", color "blue", isReclinable "false"

console.log( seat );

REVEALING MODULE➤ Advantages

➤ Cleaner approach for developers

➤ Supports private data

➤ Less clutter in the global namespace

➤ Localisation of functions and variables through

closures

➤ The syntax of our scripts are even more consistent

➤ Explicitly defined public methods and variables which

lead to increased readability

➤ Disadvantages

➤ Private methods and functions lose extendability

since they are unaccessible

➤ It's harder to patch public methods and variables that

are referred to by something private

var MyModule = ( function( window, undefined ) {

function myMethod() {

alert( 'my method' );

}

function myOtherMethod() {

alert( 'my other method' );

}

// explicitly return public methods when this object is instantiated

return {

myMethod : myMethod,

};

} )( window );

// example usage

MyModule.myMethod(); // alerts "my method"

MyModule.myOtherMethod(); // undefined

STRUCTURAL DESIGN PATTERNS

This category is concerned

with object composition and

to identify simple ways to

realise relationships

between different objects.

It helps ensure that when

one part of a system

changes, the entire structure

of the system doesn't need

to do the same.

image credit: https://addyosmani.com/resources/essentialjsdesignpatterns/book/#categoriesofdesignpatterns

THE FACADE

PATTERN➤ Advantages

➤ Enhances security for your web application

➤ Works well in combination with other

patterns

➤ Makes it easy to patch internals

➤ Provides a simpler public interface

➤ Proven useful for other major libraries such

as jQuery

➤ Disadvantages

➤ Performance impact

// a facade that masks the various browser-specific methods

function addEvent( element, event, callback ) {

if( window.addEventListener ) {

element.addEventListener( event, callback, false );

} else if( document.attachEvent ) {

element.attachEvent( 'on' + event, callback );

} else {

element[ 'on' + event ] = callback;

}

}

DECORATOR

➤ Advantages

➤ Can be used transparently

➤ Is fairly flexible

➤ Avoids the need to rely on large numbers of

subclasses to get the same benefits.

➤ Disadvantages

➤ Can complicate the application architecture if

poorly managed

➤ Introduces many small, but similar objects

into our namespace

// A vehicle constructor

function Vehicle( vehicleType ){

// some sane defaults

this.vehicleType = vehicleType || "car";

this.model = "default";

this.license = "00000-000";

}

// Test instance for a basic vehicle

var testInstance = new Vehicle( "car" );

console.log( testInstance );

// vehicle: car, model:default, license: 00000-000

var truck = new Vehicle( "truck" );

// New functionality we're decorating vehicle with

truck.setModel = function( modelName ){

this.model = modelName;

};

truck.setColor = function( color ){

this.color = color;

};

// Test the value setters and value assignment works correctly

truck.setModel( "CAT" );

truck.setColor( "blue" );

console.log( truck );

// vehicle:truck, model:CAT, color: blue

// Demonstrate "vehicle" is still unaltered

var secondInstance = new Vehicle( "car" );

console.log( secondInstance );

// vehicle: car, model:default, license: 00000-000

BEHAVIOURAL DESIGN PATTERNS

Behavioural design patterns focus on improving or streamlining through the identification of

common communication patterns between various objects

image credit:

https://addyosmani.com/

resources/essentialjsdes

ignpatterns/book/#categ

oriesofdesignpatterns

THE MEDIATOR

PATTERNThis pattern usually implements a single object that

becomes a shared resource through all of the different

pieces of an application.

➤ Advantages

➤ Reduces the communication relationship from

"many-to-many" to “many-to-one"

➤ Helps us pinpoint dependencies

➤ Excellent at decoupling objects which often

promotes smaller, reusable components

➤ Disadvantages

➤ Introduces a single point of failure

➤ When modules communicate back and forth using a

mediator pattern, it tends to become cumbersome

and usually results in a clear performance hit

var Mediator = ( function( window, undefined ) {

function Mediator() {

this._topics = {};

}

Mediator.prototype.subscribe = function mediatorSubscribe( topic, callback ) {

if( ! this._topics.hasOwnProperty( topic ) ) {

this._topics[ topic ] = [];

}

this._topics[ topic ].push( callback );

return true;

};

Mediator.prototype.unsubscribe = function mediatorUnsubscriBe( topic, callback ) {

if( ! this._topics.hasOwnProperty( topic ) ) {

return false;

}

for( var i = 0, len = this._topics[ topic ].length; i < len; i++ ) {

if( this._topics[ topic ][ i ] === callback ) {

this._topics[ topic ].splice( i, 1 );

return true;

}

}

return false;

};

Mediator.prototype.publish = function mediatorPublish() {

var args = Array.prototype.slice.call( arguments );

var topic = args.shift();

if( ! this._topics.hasOwnProperty( topic ) ) {

return false;

}

for( var i = 0, len = this._topics[ topic ].length; i < len; i++ ) {

this._topics[ topic ][ i ].apply( undefined, args );

}

return true;

};

return Mediator;

} )( window );

// example subscriber function

var listener = function ExampleListener( myVariable ) {

console.log( myVariable );

};

// example usages

var myMediator = new Mediator();

myMediator.subscribe( 'some event', listener );

myMediator.publish( 'some event', 'foo bar' ); // console logs "foo bar"

THE OBSERVER

PATTERNThis pattern implements a single object (the subject) that

maintains a reference to a collection of objects (known as

"observers") and broadcasts notifications when a change to

state occurs.

➤ Advantages

➤ Helps us pinpoint dependencies

➤ Excellent at decoupling objects which often promotes

smaller, reusable components

➤ Disadvantages

➤ Checking the integrity of your application can become

difficult

➤ Requires deeper-level thinking of the relationship

between the various components of an application

➤ Switching a subscriber from one publisher to another

can be costly

// build the Subject base class

var Subject = ( function( window, undefined ) {

function Subject() {

this._list = [];

}

// this method will handle adding observers to the internal list

Subject.prototype.observe = function observeObject( obj ) {

console.log( 'added new observer' );

this._list.push( obj );

};

Subject.prototype.unobserve = function unobserveObject( obj ) {

for( var i = 0, len = this._list.length; i < len; i++ ) {

if( this._list[ i ] === obj ) {

this._list.splice( i, 1 );

console.log( 'removed existing observer' );

return true;

}

}

return false;

};

Subject.prototype.notify = function notifyObservers() {

var args = Array.prototype.slice.call( arguments, 0 );

for( var i = 0, len = this._list.length; i < len; i++ ) {

this._list[ i ].next.apply( null, args );

}

};

return Subject;

} )( window );

// setup an object that fetchs stocks

function StockGrabber() {

var subject = new Subject();

this.addObserver = function addObserver( newObserver ) {

subject.observe( newObserver );

};

this.removeObserver = function removeObserver( deleteObserver ) {

subject.unobserve( deleteObserver );

};

this.fetchStocks = function fetchStocks() {

// fake fetching the stocks

var stocks = {

aapl : 167.00,

goog : 243.67,

msft : 99.34

};

// notify our observers of the stock change

subject.notify( stocks );

};

}

// define a couple of different observers

var StockUpdaterComponent = {

next : function() {

console.log( '"next" called on StockUpdater with: ', arguments

);

}

};

var StockChartsComponent = {

next : function() {

console.log( '"next" called on StockCharts with: ', arguments

);

}

};

// example usage

var stockApp = new StockGrabber();

stockApp.addObserver( StockUpdaterComponent );

stockApp.fetchStocks(); // console logs: "next" called on

StockUpdater with...

stockApp.addObserver( StockChartsComponent );

stockApp.fetchStocks(); // console logs: "next" called on

StockUpdater with... "next" called on StockCarts with...

stockApp.removeObserver( StockUpdaterComponent );

stockApp.fetchStocks(); // console logs: "next" called on

StockCharts with...

stockApp.removeObserver( StockChartsComponent );

stockApp.fetchStocks(); // does nothing; no observers

THE OBSERVER

PATTERNThis pattern implements a single object (the subject) that

maintains a reference to a collection of objects (known as

"observers") and broadcasts notifications when a change to

state occurs.

➤ Advantages

➤ Helps us pinpoint dependencies

➤ Excellent at decoupling objects which often promotes

smaller, reusable components

➤ Disadvantages

➤ Checking the integrity of your application can become

difficult

➤ Requires deeper-level thinking of the relationship

between the various components of an application

➤ Switching a subscriber from one publisher to another

can be costly

THE ITERATOR

PATTERNProvide a way to access the elements of an

aggregate object sequentially without exposing its

underlying representation

➤ Advantages

➤ provides a standardised way to iterate any

iterable object

➤ allows developers to design looping

constructs that are far more flexible and

sophisticated

➤ Disadvantages

➤ needs custom implementation

➤ hard to generate consistency do to lack of

native JS implementation of Interfaces

var Iterator = function(items) {

this.index = 0;

this.items = items;

}

Iterator.prototype = {

first: function() {

this.reset();

return this.next();

},

next: function() {

return this.items[this.index++];

},

hasNext: function() {

return this.index <= this.items.length;

},

reset: function() {

this.index = 0;

},

each: function(callback) {

for (var item = this.first(); this.hasNext(); item = this.next()) {

callback(item);

}

}

}

// log helper

var log = (function() {

var log = "";

return {

add: function(msg) { log += msg + "\n"; },

show: function() { alert(log); log = ""; }

}

})();

function run() {

var items = ["one", 2, "circle", true, "Applepie"];

var iter = new Iterator(items);

// using for loop

for (var item = iter.first(); iter.hasNext(); item = iter.next()) {

log.add(item);

}

log.add("");

// using Iterator's each method

iter.each(function(item) {

log.add(item);

});

log.show();

}

JS REACTIVE PROGRAM

MING“Everything is a stream”

BIG QUESTIONS

➤ What is Reactive Programming?

➤ A programming paradigm oriented around data flows and the propagation of change. This means that it

should be possible to express static or dynamic data flows with ease in the programming languages used,

and that the underlying execution model will automatically propagate changes through the data flow.

➤ Why should I consider adopting RP?

➤ It raises the level of abstraction of your code so you can focus on the interdependence of events that define

the business logic, rather than having to constantly fiddle with a large amount of implementation details.

Code in RP will likely be more concise

➤ What is a stream?

➤ Event buses or your typical click events are really an asynchronous event stream, on which you can observe

and do some side effects. Reactive is that idea on steroids. You are able to create data streams of anything,

not just from click and hover events. Streams are cheap and ubiquitous, anything can be a stream: variables,

user inputs, properties, caches, data structures, etc. You can listen to that stream and react accordingly

A HTTP REQUEST OBSERVABLE

let requestStream = Rx.Observable.just('https://api.github.com/users');

let responseStream = requestStream

.flatMap((requestUrl) => {

return Rx.Observable.fromPromise(jQuery.getJSON(requestUrl));

});

responseStream.subscribe((response) => {

// render `response` to the DOM however you wish

});

➤ What are we doing?

1. we create a simple stream from a url string

2. we subscribe / observe that stream

3. we get every event on that stream ( requestUrl ) and

make an http get request

4. then we “observe”(make a observable object) from

the promise

5. pass that to the responseStream variable

6. observe(subscribe) to the result of the http call

A DOM EVENT OBSERVABLE

let domElement = document.querySelector('#someId');

let domElementObs =

Rx.Observable.fromEvent(domElement, 'blur')

.map((ev)=>ev.target.value);

domElementObs.subscribe(ev => {

console.log(ev);

});

➤ What are we doing?

1. we are creating an observable stream from the dom

element’s “blur” event

2. we map / transform that event data so that we only

get the input’s value

3. we subscribe / observe to the event stream and

process the data.

FLATMAP IS YOUR BEST FRIEND

let source =

Rx.Observable.interval(100).take(10)

.flatMap(

(res) => {

return Rx.Observable.timer(500).map(()=>res*2)

}

);

source.subscribe(x=>console.log(x));

➤ Why?

1. It transforms multiple streams of observables into a

single observable

2. It does that by applying a rule defined by you

3. It helps you keep things clean and easy to

understand

A TYPEAHEAD SEARCH BOXfunction main() {

var $input = $('#textInput'),

$results = $('#results');

var keyup = Rx.Observable.fromEvent($input, 'keyup')

.map(function (e) {

return e.target.value;

})

.filter(function (text) {

return text.length > 2;

})

.debounce(750)

.distinctUntilChanged();

var searcher = keyup.flatMapLatest(searchWikipedia);

searcher.subscribe(

function (data) {

$results

.empty()

.append($.map(data[1], function (v) {

return $('<li>').text(v);

}));

},

function (error) {

$results

.empty()

.append($('<li>'))

.text('Error:' + error);

});

}

➤ What are we doing?

1. we are creating an observable stream from the dom

element’s “keyup” event

2. we filter that event data so that we only get values

longer than 2 keys

3. we pause for 0.75 s

4. and do all this only if the value has changed

5. use flatMap to pass the input to the http method

6. subscribe to the stream and output the result to the

dom

OBSERVABLE AND/OR OPERATIONS// Get elements

let weightSliderElem = document.querySelector('#weight-slider');

let weightTextElem = document.querySelector('#weight-text');

let heightSliderElem = document.querySelector('#height-slider');

let heightEditTextElem = document.querySelector('#height-edit-text');

let heightTextElem = document.querySelector('#height-text');

let bmiTextElem = document.querySelector('#bmi-text');

// Observables

let weight = Rx.Observable.fromEvent(weightSliderElem, 'input')

.map(ev => ev.target.value)

.startWith(weightSliderElem.value);

let height1 = Rx.Observable.fromEvent(heightSliderElem, 'input')

.map(ev => ev.target.value)

.startWith(heightSliderElem.value);

let height2 = Rx.Observable.fromEvent(heightEditTextElem, 'input')

.map(ev => parseInt(ev.target.value))

.startWith(heightEditTextElem.value);

let height = height1.merge(height2);

let bmi = weight.combineLatest(height, (w,h) => w/(h*h*0.0001));

// Subscriptions

weight.subscribe(x => weightTextElem.innerHTML = x);

height1.subscribe(x => heightTextElem.innerHTML = x);

bmi.subscribe(x => bmiTextElem.innerHTML = x);

➤ What are we doing?

1. We start by creating observable streams from dom

events

2. We create the height variable by combining multiple

observable streams into a new observable

3. We create a new observable variable by using

comblineLatest() method that emits a value ,filtered

from the source observables latest emitted values.

4. We then handle the display logic.

Code sample from André Staltz

FURTHER READING AND DOCUMENTATION

SOURCES➤ Best intro to RxJS in the world: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

➤ The documentation: http://reactivex.io/documentation/observable.html

➤ Github page: https://github.com/Reactive-Extensions/RxJS

➤ egghead.io: https://egghead.io/technologies/rx

➤ And … google it

QUESTIONS PLS…