28
JavaScript Design Patterns

Introduction to JavaScript design patterns

Embed Size (px)

DESCRIPTION

Some 30 years ago software engineers began to incorporate some of the architect Christoper Alexander's design patterns work into their own, with the crecendo being the Gang of Four's "Design Patterns: Elements Of Reusable Object-Oriented Software" written in 1995. Understanding these common proven methodologies for solving problems can accelerate development, improve maintainability, and conjure better software almost magically. Being classless, it's often difficult to directly see how classical design patterns can map to more fluid languages like JavaScript--but the language has taken quite nicely to both classical patterns and even supported the invention of some novel, never seen before patterns.

Citation preview

Page 1: Introduction to JavaScript design patterns

JavaScript Design Patterns

Page 2: Introduction to JavaScript design patterns
Page 3: Introduction to JavaScript design patterns

Itinerary1. What are patterns?

2. Why should we use them?

3. Prerequisites

4. Modules

5. Facades

6. Mediators

Page 4: Introduction to JavaScript design patterns

What is a Pattern?• A reusable solution that can be applied to commonly

occurring problems in software design.

Patterns are proven solutions.

Patterns are reusable.

Patterns are expressive.

• Socialized in our industry by the legendary GoF tome of software patterns. But from whence did they originally come?

Page 5: Introduction to JavaScript design patterns

• Christopher Alexander (born October 4, 1936 in Vienna)

• Cambridge University, BA in Architecture, MS in Mathematics. PhD from Harvard in Architecture.

• “Notes on the Synthesis of Form” was required reading in CS throughout the 60’s.

• “A Pattern Language” greatly influenced the design patterns movement.

Page 6: Introduction to JavaScript design patterns

Three Kinds• Creational: Methodologies for controlling the creation

process of objects. In classical languages these are prolific.

• Structural: Methodologies for organizing code into minimally coupled functional units and realize relationships between different objects.

• Behavioral: Methodologies that focus on streamlining communication between disparate objects.

Page 7: Introduction to JavaScript design patterns

Why Patterns?• Patterns can encourage us to code in a more structured

and organized fashion.

• Patterns can provide generalized solutions which are not tied to a specific problem, and some facilitate DRY principles.

• Patterns are supported by a larger force than the individual developer.

• Patterns create a language by which developers may quickly communicate.

Page 8: Introduction to JavaScript design patterns

A Quick Experiment

• Patterns build a vocabulary between developers and facilitate better communication. Writing code is an exercise in communication.

• Internal sensors are detecting a lifesign on deck 16, section 3, Jeffries tube junction 16C4-5. It appears to be a cat.

• What the strag will think is that any man that can hitch the length and breadth of the Galaxy, rough it, slum it, struggle against terrible odds, win through and still know where his towel is, is clearly a man to be reckoned with.

Page 9: Introduction to JavaScript design patterns

Prerequisites• JavaScript is classless; everything is an object. Many

“classical” design patterns do not apply or need be modified to map appropriately.

! var myObjectLiteral = { ! !! variableKey: variableValue,! !! functionKey: function () {! ! // ...! !! }!! };

<= Scoping and Closures

!function makeAdder(x) {! var numAddersMade = 0;! return function(y) {! numAddersMade++;! return x + y;! };!}!!

Object literal notation =>

Page 10: Introduction to JavaScript design patterns

Three Interoperating Patterns

Modules

Facades

Mediators

Page 11: Introduction to JavaScript design patterns

The Module Pattern• Ubiquitous—you’ve likely already seen and/or used them.

Almost every major framework uses some flavor of modules.

• There are many different flavors. We’ll go through only a couple of them.

• In all versions the central idea remains constant: group related functionality. This is the separation of concerns ideology.

• Let’s take a look at a simple implementation.

Page 12: Introduction to JavaScript design patterns

!var myNamespace = (function () {! ! var myPrivateVar, myPrivateMethod;! ! // A private counter variable! myPrivateVar = 0;! ! // A private function which logs any arguments! myPrivateMethod = function( foo ) {! console.log( foo );! };! ! return {! ! // A public variable! myPublicVar: "foo",! ! // A public function utilizing privates! myPublicFunction: function( bar ) {! ! // Increment our private counter! myPrivateVar++;! ! // Call our private method using bar! myPrivateMethod( bar );! ! }! };! !})();!

Page 13: Introduction to JavaScript design patterns

!var basketModule = (function() {! var basket = []; //private! return { //exposed to public! addItem: function(values) {! basket.push(values);! },! getItemCount: function() {! return basket.length;! },! getTotal: function(){! var q = this.getItemCount(),p=0;! while(q--){! p+= basket[q].price; ! }! return p;! }! }!}());!

!public class Basket {!! private List<Product> products;!! public void addItem(Product p) { //... }!! public int getItemCount() { //... }!! public double getTotal() { //... }!}!!!

If you’re familiar with classical compiled languages: the two pieces of code to the left are functionally equivalent.

Modules allow hiding—that is keeping your internals private.

Page 14: Introduction to JavaScript design patterns

!(function ($) {! // Module impl goes here! return {! // Return module interface here! }!}(jQuery));!!

!// myModule is now in global scope!var myModule = (function($) {! // Module impl goes here! return {! // Return module interface here! }!}(jQuery));!!

Importing other modules is quite simple.

Handling Dependencies

As is exporting your existing module.

Page 15: Introduction to JavaScript design patterns

CommonJS• There exists a standard definition for modules called

Asynchronous Module Definition (AMD) built by the original CommonJS mailing list participants.

• AMD is a maintained standard that has a good following: https://github.com/amdjs/amdjs-api/wiki/AMD

• Key dependency: no modules may be synchronously loaded. The order should not be significant.

Page 16: Introduction to JavaScript design patterns

//Calling define with module ID, dependency array, !//and factory function!define('myModule', ['dep1', 'dep2'], function (dep1, dep2) {!! //Define the module value by returning a value.! return function () {};!});

• Reserves the word “define” for creating modules and describing relationships between them.

• Supported by major frameworks such as Angular.js (look familiar?).

angular.module("myModule").factory("myNamespace.myModule", MyModule);! MyModule.$inject = [ "$http", "$cookies" ];! function MyModule($http, $cookies) { //... ! }

Page 17: Introduction to JavaScript design patterns

Augmentation• Allows for implementations of a single

module to exist in multiple files.

• Loose augmentation: modules can be loaded in any order. Overriding methods is not possible.

module.base.js

module.f1.js

module.f2.js

module.patch1.js

!var myModule = (function(module) {! var privateMethod = function() { //... }! return {! publicApi : privateMethod! }!}(myModule || {}));!

!!

Advanced Topics

Page 18: Introduction to JavaScript design patterns

var myModule = (function (me) {!! var old_moduleMethod = me.moduleMethod;!!! me.moduleMethod = function () {!! ! // method override!! };!!! return me;!}(myModule || {}));

Tight augmentation enables method overrides.

• There are novel uses for both tight and loose augmentation. We could push a hot-patch without breaking existing code.

• Tight augmentation requires an order to loading. This breaks the AMD spec, but buys you a lot of power.

• Super advanced topic: it’s also possible to share state across files: http://bit.ly/18byc2Q

Advanced Topics

Page 19: Introduction to JavaScript design patterns

Hotfixes• Tight augmentation can be used to efficiently hot fix

bugs so long as the affected module supports it.

Implement fix as a module override

Apply fix through HTML script

imports

var myModule = (function (me) {!! var broken = me.foo;!!! me.foo = function () {!! ! // patch the issue here!! };!!! return me;!}(myModule || {}));

<script src="myApp.js" />!<!-- Temporary fix! -->!<script src="myPatch.js" />

Order matters!Save as a patch file.

Deploy, but now fix the issue for

real :D.

Page 20: Introduction to JavaScript design patterns

Revealing Module• Simply a way of organizing

your code so it looks nicer.

• Easy to read and understand the interface provided by the module.

• Watch out: if a private function refers to a public function, you possibly cannot override the public function if a patch is necessary.

!var basketModule = (function() {! var basket = []; //private!! var _addItem = function(values) {! basket.push(values);! }! ! var _getItemCount = function() {! return basket.length;! }!! var _getTotal = function() {! var q = this.getItemCount(),p=0;! while(q--){! p+= basket[q].price; ! }! return p;! }!! // Nice, clean interface! return { ! addItem: _addItem,! getItemCount: _getItemCount,! getTotal: _getTotal! }!}());!

Page 21: Introduction to JavaScript design patterns

Facades• A facade is a limited abstraction on top of a more

complex body of code.

• Consumers interacting with the facade often interact with a very simple API hiding a complex sub-system.

• A facade also decouples interfaces from implementation—allowing for hot-swappable sub-systems.

• Only expose existing behavior.

Page 22: Introduction to JavaScript design patterns

var ecommerceFacade = (function (basketModule, promoModule) {! return {! getTotal : function () {! return basketModule.getTotal() * (1 - promoModule.getPercentDiscount());! },! listPromoCodes : promoModule.listPromoCodes,! getPercentDiscount : promoModule.getPercentDiscount,! getItemCount : basketModule.getItemCount,! addItem : basketModule.addItem,! addPromoCode : basketModule.addPromoCode! }!}(basketModule, promoModule));!

Facades should be used to further decouple business logic by supplying a common, well known interface on existing components.

var addMyEvent = function( el,ev,fn ){! ! if( el.addEventListener ){! el.addEventListener( ev,fn, false );! }else if(el.attachEvent){! el.attachEvent( "on" + ev, fn );! } else{! el["on" + ev] = fn;! }!};!

They are often useful for hiding ugly browser-based logic switches.

Page 23: Introduction to JavaScript design patterns

Mediators• Mediators encapsulate how a set of objects interact.

• This promotes loose coupling by keeping objects from referring to each other explicitly and allows you to vary their interaction independently.

• Mediators promote high cohesion by keeping code for interaction between modules encapsulated.

• Think of them as “workflow” facilitators.

Page 24: Introduction to JavaScript design patterns

Mediators encapsulate complex interactions between actors (modules) in the system. The facilitate communication without the need for directly referencing the desired component.

Example Flow

Order Mediator E-Commerce Facade Basket Module Promo Module Order Service

getTotal()

submitOrder()

Page 25: Introduction to JavaScript design patterns

Pub/Sub Subtleties• Mediators often look like event aggregators. They can

be confused with other similar patterns such as Observer.

• The difference between mediators and other pub/sub paradigms is simply this: where is the application logic coded?

• Mediators are “white gloved” business logic, while event aggregators are “fire and forget.”

Page 26: Introduction to JavaScript design patterns

Which Pattern(s)?• Build a component that handles interaction with a user

profile service. Actions include logging in via token based authentication and performing CRUD on user data.

• Build a payment processing component that supports multiple payment types like PayPal, credit card, or credit card reward points from your partner systems.

• Build a component that drives an interactive car designer. Note that some accessories and features may only be available at specific trim levels and geographical locations.

Page 27: Introduction to JavaScript design patterns

Parting Words• Pick the right tool for the job in your circumstances.

• Know modules inside and out. Settle on one paradigm and don’t deviate.

• Mediators are very slick—consider them when the relationships between objects becomes complex.

• These patterns can be used inside frameworks too (Angular, Backbone, etcetera).

Page 28: Introduction to JavaScript design patterns

Questions?Next time: Promises!