Thursday, May 7, 2015

Modular Pattern & OOP in JS

Factory pattern

Factory pattern is one great creational pattern. In the sample I have 2 different classes - Fulltime and Contractual employees. Based on the parameters passes, appropriate class is created and used

/** * Created by krishnan on 5/7/15. */function FullTimeEmployee(employeeOptions) {
    this.employeeName = employeeOptions.empName;
    this.employeeID = employeeOptions.empID;
    this.designation = employeeOptions.designation;
}

FullTimeEmployee.prototype.printDetails = function printDetails() {
        console.log('Employee Name: ' + this.employeeName + ', Employee ID: ' + this.employeeID +
        ', Designation: ' + this.designation);
    }

function ContractEmployee(employeeOptions) {
    this.employeeName = employeeOptions.empName;
    this.employeeID = employeeOptions.empID;
    this.designation = 'Consultant';
    this.hourlyRate = employeeOptions.hourlyRate;
}

ContractEmployee.prototype.printDetails = function printDetails() {
    console.log('Employee Name: ' + this.employeeName + ', Employee ID: ' + this.employeeID +
    ', Designation: ' + this.designation + ', Hourly Rate: ' + this.hourlyRate);
}

function EmployeeFactory() {
    return {
        employeeClass : FullTimeEmployee,
        createEmployee: function createEmployee(employeeOptions) {
            switch(employeeOptions.employeeType) {
                case "full-time":
                    this.employeeClass = FullTimeEmployee;
                    console.log('Employee is full-time');
                    break;
                case "part-time":
                    this.employeeClass = ContractEmployee;
                    console.log('Employee is part-time');
                    break;
            }
            return new this.employeeClass(employeeOptions);
        }
    };
}

var employeeOptions = {employeeType:'full-time', empName:'Sriram', empID:10, 'designation':'VP'}
var currEmployee = EmployeeFactory().createEmployee(employeeOptions);
currEmployee.printDetails();

var contEmployeeOptions = {employeeType:'part-time', empName:'Krishnan', empID:11, 'designation':'Contract',
    hourlyRate:90};
var currEmployee2 = EmployeeFactory().createEmployee(contEmployeeOptions);
currEmployee2.printDetails();

A simple Singleton in JS

/** * Created by krishnan on 5/7/15. */
var empSingleton = (function(empName, empId) {
    var instance;

    function init(empName, empId) {

        var employeeName, employeeID;
        var employeeSalary;

        function printEmpDetails() {
            employeeName = empName;
            employeeID = empId;
            console.log('Emp Name: ' + employeeName + ', Emp ID: ' + employeeID + ' his salary: ' + employeeSalary);
        }

        function calculateSalaryFor(experience, designation) {
            if(experience <= 1) {
                employeeSalary = 5000;
            } else if(experience <= 5) {
                employeeSalary = 10000;
            } else if(experience <= 10) {
                employeeSalary = 15000;
            } else if(experience <= 15) {
                employeeSalary = 20000;
            } else {
                employeeSalary = 30000;
            }
        }

        return {
            calculateSalary: calculateSalaryFor,
            empDetails: printEmpDetails        }
    }

    return {
        getInstance: function(empName, empID) {
          if(!instance) {
              instance = init(empName, empID);
          }
          return instance;
        }
    };
})();

var singletonInstance = empSingleton.getInstance('Krishnan', 20);
singletonInstance.calculateSalary(14, 'VP');
singletonInstance.empDetails();

var singletonInstance2 = empSingleton.getInstance('Shyam', 20);
singletonInstance2.calculateSalary(19, 'VP');
singletonInstance2.empDetails();

Modular Pattern and OOP in JS

Using classes in Javascript is tricky from reusability perspective. Especially if you want to use inheritance ideologies. I have had my fair bit of struggle with the concept.

To start with lets do a simple class using Modular pattern followed by conventional approach. I had issues with inheritance when I take Modular pattern route, but that could just be because of the code sample I had. I am working on it. As soon as I crack open that box, I'll add sample here.

Modular Pattern


Modular pattern provides a great way to encapsulate and abstract class objects. You can have private methods that will not be exposed. Conversely, you just let the caller invoke/look at what you want him to look at.

Here's the sample of the same

Employee.js

/** * Created by krishnan on 5/6/15. */
function Employee() {
    var empName;
    var empID;

    function login(empName, empId) {
        this.empName = empName;
        this.empID = empId;
        console.log('EMPLOYEE logged in');
    }

    function logoff() {
        console.log('EMPLOYEE signed off');
    }

    function hoursOfWork() {
        console.log('Employees need to work 8 hours a day');
    }

    function printEmployee() {
        console.log('EmpName: ' + this.empName + ' Emp ID: ' + this.empID);
    }

    return {
        login: login,
        logoff: logoff,
        hoursOfWork:hoursOfWork,
        printDetails:printEmployee    }
}

module.exports = Employee;

Consumer.js

/** * Created by krishnan on 5/7/15. */
var Employee = require('./Employee');
emp = Employee();
emp.login('Krishnan', 30);
emp.hoursOfWork();
emp.printDetails();
emp.logoff();

If you notice the Employee.js, although we have variables like empName and empID, they are not exposed to consumer classes. They only get to see the functions that operate on private properties of Employee class.

Conventional approach


Traditionally, we can achieve the same behavior by doing something like this. you can still create private variables by prefixing variables with "__". But that's not easy on eyes or bound to go wrong in one place or other. On the flip side. This mechanism is a lot more easier for inheritance over module pattern.

But that's for another day and another time

Here's a quick look on the code

Employee.js


/** * Created by krishnan on 5/6/15. */
function Employee(name, empId) {
    console.log('EMPLOYEE constructor invoked');
    this.empName = name;
    this.empId = empId;
}

Employee.prototype.login = function login() {
    console.log('Employee logged into office');
}

Employee.prototype.logoff = function logoff() {
    console.log('Employee signed off for the day');
}

Employee.prototype.hoursOfWork = function() {
    console.log('Employees should work for 8 hours a day');
}

module.exports = Employee;


ContractEmployee.js

/** * Created by krishnan on 5/6/15. */var Employee = require('./Employee'),
    inherits = require('util').inherits;

function ContractEmployee(name, empId) {
    console.log('ContractEmployee constructor invoked');
    Employee.call(this, name, empId);
}

inherits(ContractEmployee, Employee);

ContractEmployee.prototype.hourlyRate = function hourlyRate() {
    console.log('Hourly rate for contract employee is $20');
}

module.exports = ContractEmployee;

Consumer.js

/** * Created by krishnan on 5/6/15. */
var ContractEmployee = require('./ContractEmployee');

var cEmployee = new ContractEmployee('Krishnan', 30);
console.log('Class name: ' + cEmployee.constructor.name);
cEmployee.login();
cEmployee.logoff();
cEmployee.hoursOfWork();
cEmployee.hourlyRate();

No comments: