Since June has 5 Sundays in it, today I will write a bonus article :) Following our last week's article about Publish and Subscribe Pattern with Postal.js, I'd like to broaden the topic talking about design patterns. The design patterns from the Gang of Four book offer solutions to common problems related to the object-oriented software design. It has been in use for decades and implemented in all server side languages. It's time we use them with JavaScript and start to write some piece of decent code. I'll be writing a series of articles covering all of them. As in the book, we'll be starting with creational patterns and cover both Abstract Factory and Factory Method patterns.
Abstract classes and interfaces enforce consistent interfaces in derived classes. In JavaScript we must ensure this consistency ourselves by making sure that each 'concrete' object has the same interface definition (i.e. properties and methods) as the others.
Abstract Factory Pattern
The Abstract Factory Pattern provides an interface for creating specific factories of dependent objects without specifying their concrete class. Have a look at the illustration, depicting the pattern using UML.
Observe how Client is only aware of IMotherBoard and ICPU interfaces. The concrete factory responsible for creating the instances implementing these interfaces is also unknown. In JavaScript we don't have interfaces, but it doesn't mean that we cannot use the design pattern. Behold:
Observe how Client is only aware of IMotherBoard and ICPU interfaces. The concrete factory responsible for creating the instances implementing these interfaces is also unknown. In JavaScript we don't have interfaces, but it doesn't mean that we cannot use the design pattern. Behold:
function FastCPU() { this.performOperation = function() { console.log("Operation will perform quickly"); } } function SlowCPU() { this.performOperation = function() { console.log("Operation will perform slowly"); } } function ExpensiveMotherBoard() { this.storeData = function() { console.log("There is a lot of RAM to store the data"); } } function CheapMotherBoard() { this.storeData = function() { console.log("Little RAM. Swap file is used"); } } function HighBudgetMachine() { this.getCPU = function() { return new FastCPU(); } this.getMotherBoard = function() { return new ExpensiveMotherBoard(); } } function LowBudgetMachine() { this.getCPU = function() { return new SlowCPU(); } this.getMotherBoard = function() { return new CheapMotherBoard(); } }All our classes implement the appropriate "ghost" interfaces. Now let's build our client:
function Client() { this.assembleMachine = function(factory) { var cpu = factory.getCPU(), board = factory.getMotherBoard(); // test the machine cpu.performOperation(); board.storeData(); } } var client = new Client(); client.assembleMachine(new HighBudgetMachine());The output of course will be:
Operation will perform quickly There is a lot of RAM to store the data
Factory Method Pattern
The Factory Method pattern is a way of creating objects, but letting subclasses decide exactly which class to instantiate. Various subclasses might implement the interface; the Factory Method instantiates the appropriate subclass based on information supplied by the client or extracted from the current state. Please review the illustration below and the involved entities. Here we have interface ISorter, which defines common behaviour of our implementing classes, BubbleSorter and MergeSorter. Client uses our factory Creator, which instantiates appropriate class.
Now in JavaScript:function BubbleSorter() { this.sort = function (arr) { return "product A"; } } function MergeSorter() { this.sort = function (arr) { return "product B"; } } function Creator() { this.create = function(num) { if (num % 2 === 0) { return new BubbleSorter(); } return new MergeSorter(); } } var i, product, c = new Creator(); for (i = 0; i < 2; i += 1) { sorter = c.create(i); console.log(sorter.sort([5,2,7,3])); }Which produces the results:
bubble sorted merge sorted
Conslusion
In Abstract Factory pattern, client is unaware of the specific factory used to instantiate the needed class, where as in Factory Method, a known factory is used.
Next time we'll continue our discussion about JavaScript design patterns and will cover additional creational patterns.