XenonStack Recommends

Enterprise Digital Platform

Top 3 Design Pattern in TypeScript and JavaScript

Navdeep Singh Gill | 21 January 2022

 Design Pattern in Typescript and Javascript

What is a Design Pattern?

Design patterns are conventional answers to common software design challenges. Each pattern is similar to a blueprint that you may modify to tackle a specific design challenge in your code.

All designers need to be aware of these latest design trends. Click to explore about, UX UI Designs and Latest Trends

What are the types of design patter?

There are mainly 3 patterns:

  • Creational Pattern: Give object creation tools that improve the flexibility and reusability of current code.
  • Structural Pattern: Demonstrate how to combine objects and classes to create larger structures while keeping the structures flexible and efficient.
  • Behavioral Pattern: Take care of good communication and the delegation of responsibility among objects.

Creational

Structural

Behavioural

Factory Method

Adapter

Chain of responsibility

Abstract Factory

Bridge

Command

Builder

Composite

Iterator & Observer 

Prototype

Decorator

Mediator

Singleton..

Facade..

Memento ...

We will focus on a few patterns in this blog.

Creational Pattern

Object creation mechanisms are provided by creational patterns, which increase the flexibility and reuse of existing code.

Factory Method

One of the most prevalent creation patterns is the factory pattern. The factory implementation is in charge of creating object instances. The interface hides the implementation details from client code, resulting in the loose coupling of the application.

Usage: To reduce coupling between parent class & subclasses

abstract class Creator {
public abstract factoryMethod(): PaymentGateway;
public paymentOperation(): string {
const upiApp = this.factoryMethod();
return
`Creator: The same payment creator's code worked with ${upiApp.pay()}`;
}
}

class GPayApp extends Creator {
public factoryMethod(): PaymentGateway {
return new GPayApp();
}
}

class PhonePayApp extends Creator {
public factoryMethod(): PaymentGateway {
return new PhonePay();
}
}

interface PaymentGateway {
pay(): string;
}

class GPayAppPayment implements PaymentGateway {
Public pay(): string {
return '{Paid with GPayApp}';
}
}

class PhonePayAppPayment implements PaymentGateway {
public pay(): string {
return '{Paid with PhonePeApp}';
}
}

A few terms to keep in mind before starting SOLID are the principle of least knowledge, coupling, and cohesion. Click to explore about, SOLID Principles in JavaScript and TypeScript

Builder

Builder is a creational design pattern that enables the gradual construction of complicated objects.

Usage: When you need to construct an object with many configuration choices, this method comes in handy.

export interface PC {
name: string;
storageCapacity: number;
graphicCard: string;
}

export class PCBuilder {
private readonly _pc: PC;

constructor() {
this._pc = {
name: "",
storageCapacity: 0,
graphicCard: 0,
};
}

name(name: string): PCBuilder {
this._pc.name = name;
return this;
}

storageCapacity (storageCapacity: number): UserBuilder {
this._pc.storageCapacity: = storageCapacity:;
return this;
}

graphicCard (graphicCard: number): UserBuilder {
this._pc.graphicCard: = graphicCard;
return this;
}

build(): PC { return this._pc; }
}


const pcWithGraphicCard:PC = new PCBuilder()
.name("Dell XPS")
.graphicCard("NVIDIA GEFORCE GTX 3070")
.build();

Singleton

Singleton is a creational design pattern that assures that only one object of its kind exists and gives other code a single point of access to it.

Usage: If you want to prevent multiple instances of the same class. Real-world example: Services in angular

class Singleton {
private static dbInstance: Singleton;

private constructor() { }

public static getDbInstance(): Singleton {
if (!Singleton.dbInstance) {
Singleton.dbInstance = new Singleton();
}

return Singleton.dbInstance;
}

public getDBConnection() {
return “You are connected”
}
}

function createDBInstance() {
const first_instance = Singleton.getDbInstance();
const second_instance = Singleton.getDbInstance();

return s1 === s2 ? console.log('Single DB instance created');
: console.log('More Instance of a DB created');
}

clientCode();


Single DB instance created

A UX designer would apply similar ideas to producing a mobile app, just as an architect could design a house or building to make it easy for the occupant to explore the space. Click to explore about, User Experience in Software Product Development

Structural Pattern

Structural patterns describe how to put items and classes together to form larger structures that are both flexible and efficient.

Adapter Pattern

The adapter design pattern is a structural design pattern that allows items with mismatched interfaces to work together.

Usage: make new code compatible with Legacy code.

Few terms to be aware of:

  • Client class containing application logic
  • Adaptee is the class that is incompatible with the Target interface.
  • The adapter is the class that allows the client to use it.

class Target {
public request(): string {
return 'Target: The default target\'s behavior.';
}
}

class Adaptee {
public specificRequest(): string {
return '.eetpadA eht fo roivaheb laicepS';
}
}

class Adapter extends Target {
private adaptee: Adaptee;
constructor(adaptee: Adaptee) {
super();
this.adaptee = adaptee;
}

public request(): string {
const result = this.adaptee.specificRequest().split('').reverse().join('');
return `Adapter: (TRANSLATED) ${result}`;
}
}

function clientCode(target: Target) {
console.log(target.request());
}

const target = new Target();
clientCode(target);

const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
clientCode(adapter);

Facade

The facade may be detected in a class with a simple interface but delegates most of the work to other classes. Usually, facades manage the complete life cycle of things they employ.

Usage: It comes in helpful, especially when working with sophisticated frameworks and APIs, as it creates abstraction and lets you focus on the main thing

Think of an App System which has to:

  • connect to a DB
  • show data in UI
  • update a record
  • Refresh data in UI

The facade can be used here to handle all of this subtask & create abstraction

class Facade {
Protected databaseSystem: DataBaseSystem;
Protected uiSystem: UISystem;
constructor(databaseSystem: DataBaseSystem = null, uiSystem: UiSystem = null) {
this.databaseSystem = databaseSystem || new DataBaseSystem();
this.uiSystem = uiSystem || new UiSystem();
}

public operation(): string {
console.log('Facade initializes subsystems UI & DB ::');
this.databaseSystem.connectPostgres()
this.uiSystem.showData();

result += this.databaseSystem.updateRecord();
result += this.uiSystem.refreshUIData();

return result;
}
}


class DataBaseSystem {
public connectPostgres(): string {
return 'System connected to PostgresDB';
}
public updateRecord(): string {
return 'update Record In DB';
}
}

class UISytem{
public showData(): string {
return 'Showing Data in Form of Tables';
}

public refreshUIData(): string {
return 'UI data is refreshing….';
}

}

function App(facade: Facade) {
console.log(facade.operation());
}

const databaseSystem = new DataBaseSystem();
const uiSystem = new UiSystem();

const facade = new Facade(databaseSystem, uiSystem);
App(facade);

Behavioural Pattern

Observer

The observer is a behavioral design technique that lets some things send notifications to other objects when their state changes.

Usage: To notify all objects who are subscribing if any event occurs. RxJS used in angular is an example where this pattern is used

interface Isubject {
subscribe(observer: Observer): void;// Attach an observer to the subject.
unSubscribe(observer: Observer): void; //Detach observer from the subject.
next(): void; // Notify all observers about an event.
}

interface Observer {
update(subject: Subject): void;
}

class Subject implements Isubject {
public state: number;
private observers: Observer[] = []; //List of subscribers
public subscribe(observer: Observer): void {
const isExist = this.observers.includes(observer);
if (isExist) {
return console.log('Subject: Observer has already subscribed');
}
console.log('Subject: Observer Subscribed');
this.observers.push(observer);
}

public unSubscribe(observer: Observer): void {
const observerIndex = this.observers.indexOf(observer);
if (observerIndex === -1) {
return console.log('Subject: Non existent observer.');
}
this.observers.splice(observerIndex, 1);
console.log('Subject: Unsubscribed.');
}


public next(): void {
console.log('Subject: Notifying observers...');
for (const observer of this.observers) {
observer.update(this);
}
}

public doStuff(): void {
const MAX_VAL = 10;
this.state = Math.floor(Math.random() * MAX_VAL);
console.log(`Subject: State Changed`);
this.next();
}
}

class ObserverA implements Observer {
public update(subject: Subject): void {
if (subject instanceof Subject) {
console.log('ObserverA Triggered');
}
}
}

class ObserverB implements Observer {
public update(subject: Subject): void {
if (subject instanceof Subject) {
console.log('ObserverB Triggered');
}
}
}


const subject = new Subject();

const observer1 = new ObserverA();
subject.subscribe(observer1);

const observer2 = new ObserverB();
subject.subscribe(observer2);

subject.doStuff();
subject.doStuff();

subject.unSubscribe(observer2);
subject.doStruff();

Java vs Kotlin
Managed services for Enterprises to facilitate Automated Security Alerts, Single Click Deployments and Monitoring Solutions. Click for our Product Design and User Experience Solutions

Conclusion

We are doing all these mainly to achieve Loose coupling, High Cohesion, Reusability, and Adaptability of old and new code.