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();

ElectronJS + Vue.js: Setting up ipcRenderer event handlers

When utilizing the ipcRenderer within a Vue.js view component, make sure you register the IPC event handler once for that component using ‘ipcRenderer.on’ in the ‘mounted’ lifecycle event of the Vue.js component.

If you make the mistake of placing the IPC event handler registration (ipcRenderer.on) inside a function that is called more than once within your component, you will notice a strange side-effect. What you will end up experiencing is that each time your function is called, the ipcRenderer will register a new event handler for the named event and each time that event is called you will see the handler executed X-number of times, with X equal to the number of times the function that contains ‘ipcRenderer.on’ has been called within your component.

If you are not careful, this is an easy mistake to make and a time consuming one to figure out.


 mounted: function () {
    this.$nextTick(function () {
        //Register IPC Renderer event handles once for this control
        ipcRenderer.on('name-of-your-event', (e, args) {
            // TODO: Hanlde the event broadcast here
        });
    });
}

Ubuntu + Rmagick + Gem

 

If you run into trouble installing Rmagick on Ubuntu, it is most likely because you do not have the dev libraries installed that are needed by the rmagick Ruby Gem. Run the following commands in your terminal and everything should work fine.

sudo apt-get install graphicsmagick-libmagick-dev-compat
sudo apt-get install imagemagick
sudo apt-get install libmagickcore-dev
sudo apt-get install libmagickwand-dev
gem install rmagick

Rails 4: gem install pg (when using the Postgresql App on OS X)

If you are getting a build error from Gem when trying to install the PostgreSQL gem called ‘pg’ and you are using the PostgreSQL App instead of installing PostgreSQL via Brew or some other method, it is because Gem cannot find the PostgreSQL config in the standard locations. To correct this you need to provide Gem with the path to the config file stored within the PostgreSQL App’s internal folder structure.

This can be done as follows:

gem install pg -- --with-pg-config=/Applications/Postgres.app/Contents/Versions/9.4/bin/pg_config

Another way to accomplish this is:

export CONFIGURE_ARGS=”with-pg-include=/Applications/Postgres.app/Contents/Versions/9.4/include/”
gem install pg

Note: the latest version of the PostgreSQL App, at the time of this writing was 9.4, your version may be different. Make sure you change the 9.4 in the config path in the command above to the one you are using or it will not work. You can verify the path by opening Finder, going to the Applications folder, locating Postgres.app, right clicking and selecting ‘show package contents’. This will open up the app’s folder structure in Finder.