JavaScript Hackish Design Patterns

Muhammad Faizan

What are patterns anyway?

  • Reusable solution to a problem that occurs commonly
  • Patterns are proven solution
  • Pattern can express the solution with easy vocabulary

Types of Patterns

  • Behavioral Design Pattern

  • Structural Design Pattern

  • Creational Design Pattern

Module Pattern

  • Provides a solid structure for private and public variables
  • Easily shareable and reusable
const myCounterModule = (() => {
   let counter = 0;

   return {
     increment: () =>  ++counter,
     decrement: () => --counter,
     reset: () => counter = 0;
   }
})();

Chain of Responsibility

  • Separates concern of tasks
  • Keeps code more concise & readable
class Programmer { 
 constructor (name = "Unknown", language = "Javascript") { 
   this.name = name;
   this.language = language;
 }
 code () { 
  console.log(`${this.name} is coding in ${this.language}...`);
  return this;
 }
 sleep () {
    console.log(`${this.name} is sleeping...`);
    return this;
 }
 eat () {
   console.log(`${this.name} is eating...`);
   return this;
 } 
}

// Notice how i can chain functions
let f = new Programmer('Faizan')

// routine of how i spend my day
f.code().eat().code().sleep()
class Programmer { 
 constructor (name = "Unknown", language = "Javascript") { 
   this.name = name;
   this.language = language;
 }
 code () { 
  console.log(`${this.name} is coding in ${this.language}...`);
 }
 sleep () {
    console.log(`${this.name} is sleeping...`);
 }
 eat () {
   console.log(`${this.name} is eating...`);
 } 
}

let f = new Programmer('Faizan')

// routine of how i spend my day
f.eat(); // Faizan is eating...
f.code(); // Faizan is coding in Javascript...
f.sleep(); // Faizan is sleeping...

Chain of Responsibility

  • Separates concern of tasks
  • Keeps code more concise & readable
const Validate = require('../validation-helper');

module.exports = function (request) {
    
    const validate = Validate(request,[
        {
            field: 'channelId',
            message: 'channelId ID required'
        }
    ]);

    let getVideoListQuery = () => self.db.table('videos')
            .filter({ channelId: request.channelId })
            .run()
    );

    let sendResult = (videos) => Promise.resolve({ videos: videos });
    // Chaining here
    return validate
        .then(getVideoListQuery)
        .then(sendResult)
};

Factory Pattern

  • Easy to create multiple type with single function
  • Single change can modify all the classes later constructed
const ErrorFactory = (name, code) => class CustomError extends Error {
  /**
   *
   * @param {Error|String} error
   * @param {Object} options
   * @param {String} options.errorCode
   * @param {Object} options.details
   * @param {Object} options.details.fields
   * @param {Object} options.details.params
   * @param {Object} options.details.query
   */
  constructor(error, options) {
    let message;
    let errorCode;
    let details;
    if (options) {
      ({ errorCode, details } = options);
    }
    if (error.constructor === Error) {
      ({ message } = error);
    } else {
      message = error;
    }
    super(message);
    this.name = name || 'CustomError';
    this.status = code || 500;
    this.errorCode = errorCode;
    this.details = details;
    if (typeof Error.captureStackTrace === 'function') {
      Error.captureStackTrace(this, this.constructor);
    } else {
      this.stack = (new Error(message)).stack;
    }
  }
};

module.exports = {
  NoContent: ErrorFactory('NoContent', 204),
  BadRequestError: ErrorFactory('BadRequestError', 400),
  AddressVerificationError: ErrorFactory('AddressVerificationError', 400),
  InvalidTypeError: ErrorFactory('InvalidTypeError', 400),
  InvalidValueError: ErrorFactory('InvalidValueError', 400),
  InvalidParametersError: ErrorFactory('InvalidParametersError', 400),
  UnauthorizedError: ErrorFactory('UnauthorizedError', 401),
  InvalidCredentialsError: ErrorFactory('InvalidCredentialsError', 401),
  PaymentRequiredError: ErrorFactory('PaymentRequiredError', 402),
  ForbiddenError: ErrorFactory('ForbiddenError', 403),
  NotFound: ErrorFactory('NotFound', 404),
  UpgradeRequiredError: ErrorFactory('UpgradeRequiredError', 406),
  ConflictError: ErrorFactory('ConflictError', 409),
  RangeError: ErrorFactory('RangeError', 412),
  PreconditionError: ErrorFactory('PreconditionError', 412),
  UnprocessableEntityError: ErrorFactory('UnprocessableEntityError', 422),
  BadReferenceGiven: ErrorFactory('BadReferenceGiven', 424),
  ServerError: ErrorFactory('ServerError', 500),
  DatabaseError: ErrorFactory('DatabaseError', 500),
  NotImplementedError: ErrorFactory('NotImplementedError', 501),
  ServiceUnavailableError: ErrorFactory('ServiceUnavailableError', 503),
  SecurityError: ErrorFactory('SecurityError', 600),
};

Façade Pattern

  • Delegates action to sub-sytems that takes appropriate action.
  • Best used for APIs and functions with multiple calls
const helper = require('./helper')
Class CareemRide {
   constructor (rideInfo) { ... }
   getCharges: () => { 
        // charges are calculated seperately
        let charges =  helper.calculateCharges(this.distance, this.waiting, this.carType);
        
        // Logic of applying peakFactor is hidden under 'helper' method;
        charges = helper.applyPeakFactor(charges, this.peakFactor);
        // Returns true if coupon is valid
        if (helper.coupon.validate(this.coupon)){ 
            charges = helper.coupon.discount(charges, this.coupon)
        }
        // Helper methode 'deductFromWallet' will deduct amount 
        // from user's wallet and update user's account
        charges = helper.deductFromWallet(charges, this.user);
        return charges;
        
   }

}

const ride1 = new CareemRide({ 
    user: {
        name: 'Faizan',
        wallet: 100,
    },
    carType: 'GoPlus',
    distance: 12,
    waiting: 15,
    peakFactor: 1.9,
    coupon: '50Back'
})

display(ride1.getCharges());

PubSub Pattern

  • Used to pass information about update of subject to subscribed handler
  • Used for watching elements update
Made with Slides.com