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
Javascript Hacks
By Muhammad Faizan
Javascript Hacks
For Angular Pakistan (Dot Zero Presentation)
- 134