JavaScript: Design Patterns 08 – Abstract Factory Pattern

Abstract Factory Pattern

This is an enhancement of the Factory Pattern, in that it operates in basically
the same way, the factory creates objects based on the passed in arguments.
The difference lies in that additional abstraction is added to the factory.

The added abstraction

The purpose of the traditional factory pattern is to remove, from the codebase,
the redundant object creation code by encapsulating it within a reusable object
the factory.

The abstract factory pattern takes this a step further by removing from the
factory its knowledge of the objects it is creating, making the factory extremely
generic. Due to this level of abstraction, it becomes important that the objects
the factory will be building are first registered with the factory prior to the
factory being used. The registration of objects can be done:

  • Internally to the factory in the init function
  • Dynamically through factory configuration passed to the factory by the caller
  • Through a registration function on the factory to which constructor functions are
    passed by the caller

How the registration occurs is totally up to the needs of the system.

Script: Supplemental for Example Below
// Constructor functions that will be used inside of the Lizard Factory
function Lizard(args) {
this.kind = args.kind;
this.moves = args.moves;
}
Lizard.prototype.bites = function() {
console.log(`${this.kind} just bit me really hard as it ${this.moves} away.`);
};
Lizard.prototype.says = function() {
console.log(`I am a ${this.kind}.`);
};

function Chameleon(args) {
Lizard.call(this, { kind: 'Chameleon', moves: args.moves });
this.name = args.name;
this.color = 'green'
}
Chameleon.prototype = Object.create(Lizard.prototype);
Chameleon.prototype.changeColor = function(color){
this.color = color;
console.log(`${this.kind} just changed color to ${this.color}, you no see me!`);
}

function Dragon(args) {
Lizard.call(this, { kind: 'Dragon', moves: args.moves });
this.name = args.name;
}
Dragon.prototype = Object.create(Lizard.prototype);
Dragon.prototype.fireBreath = function(){
console.log(`${this.kind} breath, you are now on fire.`);
}
Script: Abstract Factory Example
// Abstract and more generic animal factory
let AnimalFactory = function(){
// Object to hold registered types with associated constructor functions
this.types = {};

// object creation function becomes more generic
this.create = function(type, args){
return new this.types[type](args);
};

// type + constructor registration method
this.register = function(type, builder){
this.types[type] = builder;
};
}

// in this example, we register the types that can be created by the factory
// externally using the register method of the factory
let factory = new AnimalFactory();
factory.register('lizard', Lizard);
factory.register('chameleon', Chameleon);
factory.register('dragon', Dragon);

let baseLizard = factory.create('lizard', { kind: 'lizard', moves: 'speedy little demon' });
console.log(baseLizard);

let myChameleon = factory.create('chameleon', { name: 'Freddy', moves: 'very very slow' });
console.log(myChameleon);
myChameleon.changeColor('red');
let myDragon = factory.create('dragon', { name: 'Freddy', moves: 'very very slow' });
console.log(myDragon);
myDragon.fireBreath();

JavaScript: Design Patterns 07 – Factory Pattern

Factory Pattern

Short Def: Used to encapsulate the creation logic for a related family of
objects.

Long Def: Used commonly to simplify object creation in systems where numerous similar objects are
dynamically created based on a few differing properties. The pattern takes redundant
object creation code that might be scattered throughout a system and wraps that
code in a single object in a reusable form that can be used across the system.

Another key attribute of this pattern is that the factory encapsulates the logic
needed to create objects of different types based on the arguments provided. This
removes the need for the caller to be concerned with specifying the object type
needed and it therefore relies on the factory to provide what it needs.

Script: Supplemental for Example Below
// Constructor functions that will be used inside of the Lizard Factory
function Lizard(kind, moves) {
this.kind = kind;
this.moves = moves;
}

Lizard.prototype.bites = function() {
console.log(`${this.kind} just bit me really hard as it ${this.moves} away.`);
};

Lizard.prototype.says = function() {
console.log(`I am a ${this.kind}.`);
};

function Chameleon(name, moves) {
Lizard.call(this, 'Chameleon', moves);
this.name = name;
this.color = 'green'
}

Chameleon.prototype = Object.create(Lizard.prototype);

Chameleon.prototype.changeColor = function(color){
this.color = color;
console.log(`${this.kind} just changed color to ${this.color}, you no see me!`);
};

function Dragon(name, moves) {
Lizard.call(this, 'Dragon', moves);
this.name = name;
}

Dragon.prototype = Object.create(Lizard.prototype);

Dragon.prototype.fireBreath = function(){
console.log(`${this.kind} breath, you are now on fire.`);
};

Script: Factory Example
// Lizard Factory: setup to select the appropriate object type to create
// based on a keyword passed in followed by an arguments array that is
// passed to the appropriate object constructor function.
let LizardFactory = function(){
this.create = function(type, args){
switch(type){
case 'chameleon':
return new Chameleon(...args);
case 'dragon':
return new Dragon(...args);
default:
return new Lizard(...args);
break;
}
};
};

let factory = new LizardFactory();

let baseLizard = factory.create(null, ['base lizard', 'speedy little demon']);
console.log(baseLizard);

let myChameleon = factory.create('chameleon', ['Freddy', 'very very slow']);
myChameleon.changeColor('red');
console.log(myChameleon);

let myDragon = factory.create('dragon', ['Freddy', 'very very slow']);
myDragon.fireBreath();
console.log(myDragon);

JavaScript: Design Patterns 06 – Singleton Pattern

Singleton Pattern

Used when only a single instance of an object should exists and that instance
should be the one used no matter who or what is calling or using
the state or abilities of that object type. Used mostly when a system requires
an orchestrating object to hold state or have abilities that must be shared
across the entire system.

Benefits
– Delayed instantiation: the singleton object is not created until the first calling
to the object is received and a reference to that object is then held for the life
of the system’s execution.
– Exists across the entire system: the singleton when subsequently called
for use, will provide the caller a reference to the exact object instance that was
created upon first call.
– Consistent interface for accessing the singleton object: there is a function or object
that handles the creation of and holds the reference to the single object instance.

let dataContextSingleton = (function(){
// Holds the reference to the singleton object instance
var singletonInstance;

// Private function used to build the singleton object instance if it
// does not yet exist
function init(){

// Private state/functions for singleton object
let _objectCollection = [];

// Public state/functions
function add(obj){
_objectCollection.push(obj);
}

function find(id){
let found = [];
for(let obj of _objectCollection){
if(obj.id === id) found.push(obj);
}
return found;
}

// Using module reveal pattern to expose API
return {
add,
find
};
}

// Returns the singleton management object used to get a reference to the
// the singleton object instance
return {
getInstance: function(){
// if singleton instance does not exist, create it
if(!singletonInstance){
singletonInstance = init();
}
return singletonInstance;
}
}
})();

let firstContext = dataContextSingleton.getInstance();
firstContext.add({
id: 1,
name: 'test'
});

let secondContext = dataContextSingleton.getInstance();
let found = secondContext.find(1);

// When printed to the console, will see that the found array contains
// the object with id 1 that was added by the first reference to the
// dataContextSingleton.
console.log(found);

JavaScript: Design Patterns 05 – Controlling Global Scope Access

Controlling Global Scope Access

Utilizing the Module Patterns, we can control access to the Global Scope
in the following ways:

  • Using Modules to make everything private
  • Conditionally add elements to the Global Scope
  • Send things from the Global Scope into your custom module

This is accomplished by wrapping the library with an IIFE and anonymous function
and pass everything the library needs to run into that IIFE for use. This removes
your library completely out of the Global Scope.

(function(window, document, $){
// IIFE will execute and return an object literal
var myLibrary = (function(){

// Private scope as everything within the anonymous function is hidden
// Private variables
let _privateData = 'this is private stuff';

// Private functions
function _doSomethingPrivate(){
console.log('Just did something private.');
}

// Public API functions
function doThis(){
console.log('This was done.');
}
function doThat(){
console.log('That was done.');
doSomethingPrivate();
}

// The returned object contains the public API to the library
return {
doThis,
doThat
}

})();

// within this private scope, library api can be executed
$(document).ready(function(){
myLibrary.doThis();
myLibrary.doThat();
});

/* within this private scope, the library api can be added to the window
object so that it can be used externally.*/
if(!window.myLibrary) window.myLibrary = myLibrary;

// passing in only the Global Scope objects we need for our library to function
})(window, document, jQuery);

JavaScript: Design Patterns 04 – Module Reveal Pattern

Module Reveal Pattern

Solves the following problems within the Module Pattern

  • Ensuring private members cannot interact with public ones
  • Reduce complexity introduced by the Module Pattern

Unlike the Module Pattern, the Module Reveal Pattern, does not place functions
and variables in the returned API object directly. Everything is created
within the private scope of the anonymous function.

Since both public and private members are within the same scope now, it is
important to differentiate between the two. This is commonly done by placing
an underscore in front of variable names that are private, to note them as such.
This of course requires that the developer be extra careful when using and
calling variables, making sure he is using the correct public or private one.

// IIFE will execute and return an object literal
var myLibrary = (function(){

// Private scope as everything within the anonymous function is hidden
// Private variables
let _privateData = 'this is private stuff';

// Private functions
function _doSomethingPrivate(){
console.log('Just did something private.')
}

// Public API functions
function doThis(){
console.log('This was done.');
}
function doThat(){
console.log('That was done.');
_doSomethingPrivate();
}

// The returned object contains the public API to the library
return {
doThis,
doThat
}

})();

// Accessible because they are on the API object
myLibrary.doThis();
myLibrary.doThat();

// NOT accessible because it is hidden within the scope of the anonymous function
// doSomethingPrivate(); // Uncommenting this line will throw an error

JavaScript: Design Patterns 03 – Module Pattern

Module Pattern

Solved the following problems in JavaScript

  • Creating access control to data that should be private
  • Providing a clear public API for custom libraries
  • True encapsulation that was not built into JavaScript to start with

Utilizing IIFEs to Control access

Immediately Invoked Functional Expression

// IIFE will execute and return an object literal
var myLibrary = (function(){

// Private scope as everything within the anonymous function is hidden
// Private variables
let privateData = 'this is private stuff';
// Private functions
function doSomethingPrivate(){
console.log('Just did something private.')
}

// The returned object contains the public API to the library
return {
doThis: function(){
console.log('This was done.');
},
doThat: function(){
console.log('That was done.');
/*
Accessible because this object was created within the scope of the
anonymous function that was executed by the IIFE.
*/
doSomethingPrivate();
}
}

})();

// Accessible because they are on the API object
myLibrary.doThis();
myLibrary.doThat();

// NOT accessible because it is hidden within the scope of the anonymous function
// doSomethingPrivate(); // Uncommenting this line will throw an error

JavaScript: Design Patterns 02 – Object Literal Pattern

Object Literal Pattern

The most basic approach to dealing with the pollution of the global scope
is to use a simple object literal to wrap a custom library.

Script One
var myLibrary = {
name: 'My super duper library',
say: function(){ return `This entire object will be shadowed...`; }
}
Script Two
var myLibrary = 'This is not even a library, it\'s a string. Boy are you hosed!';

NOTE: only applicable when using 'var' to create variables.
When using 'let', JavaScript will throw an error that the variable name has
already been taken. This includes the case where one of the variables was created
using 'var' and the other was created using 'let', JS will still throw an error.

Custom Name-Spaced Pattern

Uses object literal pattern

Although this may protect the variables, functions, and objects within your
custom library, there is still the issue that someone else may use the same
variable name for an object in their script as you used for the name of your
library's object wrapper.

To overcome this issue, it is standard practice to follow a common Java name-spacing
technique, use a registered domain name you own as your namespace.

Domain Namespace
// Build the domain structure using object literals
// checking to make sure those objects do not already exist
var com = com || {};
com.memorytin = com.memorytin || {};
com.memorytin.myLibrary = com.memorytin.myLibrary || {
name: 'My super duper library',
say: function(){ return `This entire object will be shadowed...`; }
};

Note: the above only works if you are using ‘var’ to create the initial
namespace object. If you use ‘let’, the browser will throw an error due to the
fact that you are using ‘com’ on the right side of the assignment operator and
it has not yet been instantiated.