mibu
4/30/2017 - 10:28 AM

js_design_patterns.js

/*=======================================
=            DESIGN PATTERNS            =
=======================================*/
/*==========  Creating Objects with Literals  ==========*/
var obj = {
    firstName: "Jeremy",
    lastName: "McPeak"
};

obj.firstName = "Jeremy";
obj["lastName"] = "McPeak";

var firstName = obj["firstName"];
var lastName = obj.lastName;

Object.defineProperty(obj, "country", {
    value: "USA"
});

Object.defineProperties(obj, {
    twitter: {
        value: "jwmcpeak"
    },
    email: {
        value: "jwmcpeak@wdonline.com"
    }
});
/*==========  Generating Objects with Object.create()  ==========*/
var johnDoe = {
    firstName: "John",
    lastName: "Doe",
    sayName: function() {
        return "My name is " + this.firstName + " " + this.lastName;
    }
};

var janeDoe = Object.create(johnDoe, {
    firstName: {
        value: "Jane"
    },
    greet: {
        value: function(person) {
            return "Hello, " + person.firstName;
        }
    }
});

var jimSmith = Object.create(janeDoe, {
    firstName: {
        value: "Jim"
    },
    lastName: {
        value: "Smith"
    }
});

//alert(johnDoe.sayName());
//alert(janeDoe.sayName() + " " + janeDoe.greet(johnDoe));
//alert(jimSmith.sayName() + " " + jimSmith.greet(janeDoe));

console.log(janeDoe.__proto__ === johnDoe);
console.log(jimSmith.__proto__ === janeDoe);

/*==========  The Constructor Pattern  ==========*/
function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

Person.prototype.sayName = function() {
    return "My name is " + this.firstName + " " + this.lastName;
};


var johnDoe = new Person("John", "Doe");
var janeDoe = new Person("Jane", "Doe");

var isPerson = johnDoe instanceof Person; // true
var isSame = johnDoe.sayName === janeDoe.sayName; // true

/*==========  The Inheritance Pattern  ==========*/
function Beverage(name, temperature) {
    this.name = name;
    this.temperature = temperature;
}

Beverage.prototype.drink = function() {
    console.log("I'm drinking " + this.name);
};

function Coffee(type) {
    Beverage.call(this, "coffee", "hot");
    this.type = type;
}

Coffee.prototype = Object.create(Beverage.prototype);
Coffee.prototype.sip = function() {
    console.log("Sipping some awesome " + this.type + " " + this.name);
};

var water = new Beverage("water", "cold");
var coffee = new Coffee("bold dark roast");

/*==========  Mixins  ==========*/
function extend(target) {
    if (!arguments[1]) {
        return;
    }

    for (var ii = 1, ll = arguments.length; ii < ll; ii++) {
        var source = arguments[ii];

        for (var prop in source) {
            if (!target[prop] && source.hasOwnProperty(prop)) {
                target[prop] = source[prop];
            }
        }
    }
}

function Person(name) {
    this.name = name;
}

function Dog(name) {
    this.name = name;
}

var speaker = {
    speak: function() {
        return this.name + " is speaking.";
    }
};

var mover = {
    walk: function() {
        return this.name + " is walking.";
    },
    run: function() {
        return this.name + " is running.";
    }
};

var arithmetic = {
    add: function() {
        return this.name + " is adding numbers together.";
    },
    multiply: function() {
        return this.name + " is multiplying numbers together.";
    }
};

//$.extend(Person.prototype, speaker, mover, arithmetic);
//$.extend(Dog.prototype, speaker, mover);

extend(Person.prototype, speaker, mover, arithmetic);
extend(Dog.prototype, speaker, mover);


var john = new Person("John Doe");
var fido = new Dog("Fido");

/*==========  Mixins #2  ==========*/

var toolbar = new Toolbar("myToolbar");
var toggle = document.getElementById("itemStateToggle");

toggle.addEventListener("click", function(e) {
    e.target.toggleActiveState();

    e.preventDefault();
});

function mixin(target, source, methods) {
    for (var ii = 2, ll = arguments.length; ii < ll; ii++) {
        var method = arguments[ii];

        target[method] = source[method].bind(source);
    }
}

mixin(toggle, toolbar.items[0], "toggleActiveState");

/*==========  Decorator Simple  ==========*/
function Coffee() {

}

Coffee.prototype.cost = function() {
    return 5;
};

Coffee.small = function(coffeeObj) {
    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost - 1;
    };
};

Coffee.medium = function(coffeeObj) {};

Coffee.large = function(coffeeObj) {
    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost + 1;
    };
};

Coffee.sugar = function(coffeeObj) {
    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost + .15;
    };
};

Coffee.creamer = function(coffeeObj) {
    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost + .15;
    };
};

Coffee.whippedCream = function(coffeeObj) {
    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost + .15;
    };
};

Coffee.milk = function(coffeeObj) {
    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost + .15;
    };
};

Coffee.foam = function(coffeeObj) {
    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost + .15;
    };
};

Coffee.chocolate = function(coffeeObj) {
    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost + .15;
    };
};

Coffee.mocha = function(coffeeObj) {
    Coffee.milk(coffeeObj);
    Coffee.foam(coffeeObj);
    Coffee.chocolate(coffeeObj);

    var cost = coffeeObj.cost();

    coffeeObj.cost = function() {
        return cost;
    };
};


var coffee = new Coffee();
var mocha = new Coffee();

/*==========  Decorator Prototypal  ==========*/
function Beverage() {
    this._cost = 0;
}

Beverage.prototype.cost = function() {
    return this._cost;
};

function BeverageDecorator(beverage) {
    Beverage.call(this);
    this.beverage = beverage;
}

BeverageDecorator.prototype = Object.create(Beverage.prototype);
BeverageDecorator.prototype.cost = function() {
    return this._cost + this.beverage.cost();
};

function Small(beverage) {
    BeverageDecorator.call(this, beverage);
    this._cost = -1;
}

Small.prototype = Object.create(BeverageDecorator.prototype);

function Sugar(beverage) {
    BeverageDecorator.call(this, beverage);
    this._cost = .15;
}

Sugar.prototype = Object.create(BeverageDecorator.prototype);


function Coffee() {
    Beverage.call(this);
    this._cost = 5;
}

Coffee.prototype = Object.create(Beverage.prototype);

var coffee = new Coffee();
coffee = new Small(coffee);
coffee = new Sugar(coffee);

/*==========  Module Pattern  ==========*/
var dom = (function(jq) {

    var _counter = 0;

    function generateId() {
        return "customId" + _counter++;
    }

    function create(tagName, id) {
        var el = document.createElement(tagName);

        el.id = id || generateId();

        return el;
    }

    return {
        generateId: generateId,
        create: create
    };
}(jQuery));

/*==========  Singleton  ==========*/
var dom = (function() {
    var _counter = 0;
    var instance;

    function generateId() {
        return "customId" + _counter++;
    }

    function create(tagName, id) {
        var el = document.createElement(tagName);

        el.id = id || generateId();

        return el;
    }

    function createInstance() {
        return {
            generateId: generateId,
            create: create
        };
    }

    return {
        getInstance: function() {
            return instance || (instance = createInstance());
        }
    };
}());

/*==========  The Factory  ==========*/
define(["lesson09_module"], function(dom) {

    function createInput(type) {
        var el = dom.create("input");
        el.type = type;

        return el;
    }

    var controls = {
        text: function(options) {
            var el = createInput("text");

            if (typeof options.value !== "undefined") {
                el.value = options.value;
            }

            return el;
        },
        checkbox: function(options) {
            var el = createInput("checkbox");

            if (typeof options.checked !== "undefined") {
                el.checked = options.checked;
            }

            return el;
        }
    };


    return {
        create: function(options) {
            var type = options.type ? options.type.toLowerCase() : undefined;

            if (!type || !controls[type]) {
                throw new {
                    message: type + " is not supported."
                };
            }

            return controls[type](options);
        }
    };
});

/*==========  The Command Pattern  ==========*/
function Calculator() {
    this._currentValue = 0;
    this.commands = [];
}

Calculator.prototype = {
    execute: function(command) {
        this._currentValue = command.execute(this._currentValue);
        this.commands.push(command);
    },
    undo: function() {
        var cmd = this.commands.pop();

        this._currentValue = cmd.undo(this._currentValue);
    },
    getCurrentValue: function() {
        return this._currentValue;
    }
};

function add(value) {
    return value + this.value;
}

function sub(value) {
    return value - this.value;
}

function Command(fn, undo, value) {
    this.execute = fn;
    this.value = value;
    this.undo = undo;
}

function AddCommand(value) {
    Command.call(this, add, sub, value);
}

function SubCommand(value) {
    Command.call(this, sub, add, value);
}

var calc = new Calculator();

/*==========  The Facade Pattern  ==========*/
function addEvent(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;
    }
}

function removeEvent(el, ev, fn) {
    if (el.removeEventListener) {
        el.removeEventListener(ev, fn, false);
    } else if (el.detachEvent) {
        el.detachEvent("on" + ev, fn);
    } else {
        el["on" + ev] = null;
    }
}

/*==========  The Observer Pattern  ==========*/
define(function() {

    var subscribers = {};


    function subscribe(type, fn) {
        if (!subscribers[type]) {
            subscribers[type] = [];
        }

        if (subscribers[type].indexOf(fn) == -1) {
            subscribers[type].push(fn);
        }
    }

    function unsubscribe(type, fn) {
        var listeners = subscribers[type];

        if (!listeners) {
            return;
        }

        var index = listeners.indexOf(fn);

        if (index > -1) {
            listeners.splice(index, 1);
        }
    }

    function publish(type, evtObj) {
        if (!subscribers[type]) {
            return;
        }

        if (!evtObj.type) {
            evtObj.type = type;
        }

        var listeners = subscribers[type];

        for (var ii = 0, ll = listeners.length; ii < ll; ii++) {
            listeners[ii](evtObj);
        }
    }

    return {
        subscribe: subscribe,
        unsubscribe: unsubscribe,
        publish: publish
    };
});

/*-----  End of DESIGN PATTERNS  ------*/