Search This Blog

2023/08/31

Repository Design Pattern

 const mysql = require('mysql');


// Repository interface
class UserRepository {
constructor(connection) {
this.connection = connection;
}

getById(id) {
return new Promise((resolve, reject) => {
this.connection.query('SELECT * FROM user WHERE id = ?', [id], (error, results) => {
if (error) {
reject(error);
} else {
resolve(results[0]);
}
});
});
}

save(user) {
return new Promise((resolve, reject) => {
this.connection.query('INSERT INTO user SET ?', user, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result.insertId);
}
});
});
}
}

// Create a connection to the MySQL database
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'sangram#81',
database: 'playground'
});

connection.connect();


// Client
const userRepository = new UserRepository(connection);

const user = { id: 1, name: "Alice" };
userRepository.save(user)
.then(insertedId => {
console.log(`User inserted with ID: ${insertedId}`);
return userRepository.getById(insertedId);
})
.then(retrievedUser => {
console.log("Retrieved user:", retrievedUser);
})
.catch(error => {
console.error("Error:", error);
})
.finally(() => {
connection.end(); // Close the database connection
});

Prototype Design pattern

 // Prototype interface

class Animal {
constructor(name, species) {
this.name = name;
this.species = species;
}

clone() {
return null;
}
}

// Concrete prototypes
class Dog extends Animal {
constructor(name) {
super(name, "Dog");
}

clone() {
return new Dog(this.name);
}
}

class Cat extends Animal {
constructor(name) {
super(name, "Cat");
}

clone() {
return new Cat(this.name);
}
}

// Client
const originalDog = new Dog("Buddy");
const clonedDog = originalDog.clone();

console.log(originalDog); // Dog { name: 'Buddy', species: 'Dog' }
console.log(clonedDog); // Dog { name: 'Buddy', species: 'Dog' }

Chain of responsibility

 // Handler interface

class Handler {
constructor() {
this.successor = null;
}

setSuccessor(successor) {
this.successor = successor;
}

handleRequest(request) {
if (this.successor) {
this.successor.handleRequest(request);
}
}
}

// Concrete Handlers
class ConcreteHandlerA extends Handler {
handleRequest(request) {
if (request === 'A') {
console.log('Handled by ConcreteHandlerA');
} else {
super.handleRequest(request);
}
}
}

class ConcreteHandlerB extends Handler {
handleRequest(request) {
if (request === 'B') {
console.log('Handled by ConcreteHandlerB');
} else {
super.handleRequest(request);
}
}
}

class ConcreteHandlerC extends Handler {
handleRequest(request) {
if (request === 'C') {
console.log('Handled by ConcreteHandlerC');
} else {
console.log('Request cannot be handled.');
}
}
}

// Setting up the chain
const handlerA = new ConcreteHandlerA();
const handlerB = new ConcreteHandlerB();
const handlerC = new ConcreteHandlerC();

handlerA.setSuccessor(handlerB);
handlerB.setSuccessor(handlerC);

// Client
handlerA.handleRequest('A'); // Handled by ConcreteHandlerA
handlerA.handleRequest('B'); // Handled by ConcreteHandlerB
handlerA.handleRequest('C'); // Handled by ConcreteHandlerC
handlerA.handleRequest('D'); // Request cannot be handled.

Dependency Injection using property

 // Dependency class

class Dependency {
doAction() {
console.log("Dependency is performing an action.");
}
}

// Service class that relies on a dependency
class Service {
// Dependency is provided as a property
constructor() {
this.dependency = null;
}

setDependency(dependency) {
this.dependency = dependency;
}

doSomething() {
this.dependency.doAction();
}
}

// Creating instances and injecting dependencies
const dependency = new Dependency();
const service = new Service();

// Injecting the dependency as a property
service.setDependency(dependency);

service.doSomething();

Dependency Injection using constructor

 // Service class that requires a dependency

class Service {
constructor(dependency) {
this.dependency = dependency;
}

doSomething() {
this.dependency.doAction();
}
}

// Dependency class
class Dependency {
doAction() {
console.log("Dependency is performing an action.");
}
}

// Creating instances and injecting dependencies
const dependency = new Dependency();
const service = new Service(dependency);

service.doSomething();

Iterator Design pattern

 // Iterator Interface

class Iterator {
constructor(collection) {
this.collection = collection;
this.index = 0;
}

next() {
const item = this.collection[this.index];
this.index++;
return item;
}

hasNext() {
return this.index < this.collection.length;
}
}

// Concrete Aggregate
class ListAggregate {
constructor() {
this.items = [];
}

addItem(item) {
this.items.push(item);
}

createIterator() {
return new Iterator(this.items);
}
}

// Client code
const aggregate = new ListAggregate();
aggregate.addItem("Item 1");
aggregate.addItem("Item 2");

const iterator = aggregate.createIterator();

while (iterator.hasNext()) {
console.log(iterator.next());
}

2023/08/30

Observer Design Pattern

// Subject (Observable)
class WeatherStation {
constructor() {
this.temperature = 0;
this.observers = [];
}

addObserver(observer) {
this.observers.push(observer);
}

removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}

setTemperature(temperature) {
this.temperature = temperature;
this.notifyObservers();
}

notifyObservers() {
this.observers.forEach(observer => {
observer.update(this.temperature);
});
}
}

// Observer
class Observer {
update(temperature) {}
}

// Concrete Observer
class TemperatureDisplay extends Observer {
update(temperature) {
console.log(`Temperature updated: ${temperature}°C`);
}
}

// Usage
const weatherStation = new WeatherStation();
const temperatureDisplay = new TemperatureDisplay();


weatherStation.addObserver(temperatureDisplay);

weatherStation.setTemperature(25); // Output: Temperature updated: 25°C
weatherStation.setTemperature(30); // Output: Temperature updated: 30°C

Decorator Design Pattern

 // Component Interface

class Text {
constructor(content) {
this.content = content;
}

format() {
return this.content;
}
}

// Concrete Component
class PlainText extends Text {
constructor(content) {
super(content);
}
}

// Decorator
class TextDecorator extends Text {
constructor(text) {
super();
this.text = text;
}

format() {
return this.text.format();
}
}

// Concrete Decorator
class BoldDecorator extends TextDecorator {
constructor(text) {
super(text);
}

format() {
return `<b>${this.text.format()}</b>`;
}
}

class ItalicDecorator extends TextDecorator {
constructor(text) {
super(text);
}

format() {
return `<i>${this.text.format()}</i>`;
}
}

// Usage
const plainText = new PlainText("Hello, world!");

const boldText = new BoldDecorator(plainText);
const italicBoldText = new ItalicDecorator(boldText);

console.log(plainText.format()); // Output: Hello, world!
console.log(boldText.format()); // Output: <b>Hello, world!</b>
console.log(italicBoldText.format()); // Output: <i><b>Hello, world!</b></i>

The Decorator pattern here is used to apply
formatting (bold and italic) to text content
in a flexible and extensible way. It demonstrates
how decorators can be stacked to create more complex
behaviors while keeping individual decorators reusable.

Design Patterns: Types


A design pattern is a reusable and general solution to a common software design
problem.It represents a blueprint for solving a particular class of problems in
a consistent and efficient manner. Design patterns provide a shared vocabulary
and a set of proven practices that enable software developers to communicate
more effectively, design robust and maintainable code, and avoid reinventing the
wheel for common challenges.

Design patterns capture the collective experience and expertise of software
designers and architects, offering solutions to recurring issues in software
design, such as structuring classes and objects, managing relationships between
components, and handling interactions between different parts of a system. They
help promote code that is modular, flexible, and easier to extend or modify over
time.

Design patterns are not code snippets or ready-made solutions that can be
directly copied into a project. Instead, they provide high-level descriptions of
the problem, the solution, and the context in which the pattern should be
applied. Developers need to adapt and implement the patterns according to the
specific requirements of their projects.


Design patterns are divided into three main categories:
creational, structural,and behavioral. Each category addresses a different
aspect of software design and helps solve specific design problems.

Here's a brief overview of the types of design patterns within each category:

Creational Design Patterns:
These patterns focus on object creation mechanisms, attempting to create objects
in a manner that suits the situation. Creational patterns provide control over
object instantiation.

Singleton: Ensures a class has only one instance and provides a global point of
access to that instance.

Factory Method: Creates objects without specifying the exact class of object
that will be created.

Abstract Factory: Creates families of related or dependent objects without
specifying their concrete classes.

Builder: Separates the construction of a complex object from its
representation, allowing the same construction process to create various
representations.

Prototype: Creates new objects by copying an existing instance, known as the
prototype.

Structural Design Patterns:
These patterns deal with object composition, creating relationships between
objects to form larger structures. Structural patterns provide ways to define
relationships between objects.

Adapter: Converts the interface of a class into another interface that clients
expect.

Bridge: Separates abstraction from its implementation, allowing both to evolve independently.

Composite: Composes objects into tree structures to represent part-whole
hierarchies.

Decorator: Adds behavior or responsibilities to individual objects dynamically,
without affecting other objects.

Facade: Provides a unified interface to a set of interfaces in a subsystem.

Flyweight: Shares objects to reduce memory consumption by allowing objects to
be reused instead of recreated.

Proxy: Provides a surrogate or placeholder for another object to control
access to it.

Behavioral Design Patterns:
These patterns deal with object collaboration and responsibilities, focusing
on how objects interact and communicate with each other. Behavioral patterns
provide solutions for object interaction and responsibilities.

Chain of Responsibility: Allows an object to pass a request along a chain of
potential handlers.

Command: Encapsulates a request as an object, allowing parameterization of
clients with different requests, queuing, and logging of requests.

Interpreter: Provides a way to evaluate language grammar or expressions.

Iterator: Provides a way to access elements of a collection sequentially
without exposing its underlying representation.

Mediator: Reduces the coupling between components by allowing them to
communicate through a central mediator.

Memento: Captures an object's internal state to be restored later, without
exposing its internal structure.

Observer: Defines a dependency between objects so that when one object changes
state, all its dependents are notified and updated automatically.

State: Allows an object to change its behavior when its internal state changes.

Strategy: Defines a family of algorithms, encapsulates each one, and makes
them interchangeable.

Template Method: Defines the structure of an algorithm in the base class but
lets subclasses override specific steps.

Visitor: Lets you add further operations to objects without having to modify
them.

Each design pattern is a valuable tool in a programmer's toolbox, offering
standardized solutions to common design problems and promoting software
that's easier to understand, maintain, and scale.

Proxy Design Pattern

 //image class created just when it needed


// Subject: Image
class Image {
constructor(url) {
this.url = url;
}

display() {
console.log(`Displaying image from ${this.url}`);
}
}

// Proxy: Virtual Image Proxy
class ImageProxy {
constructor(url) {
this.url = url;
this.realImage = null;
}

display() {
if (this.realImage === null) {
this.realImage = new Image(this.url);
}
this.realImage.display();
}
}

// Usage
const imageProxy = new ImageProxy('image.jpg');

// Image is not loaded yet
imageProxy.display();

// Image is loaded and displayed
imageProxy.display();

adapter design pattern in javascript

 // External API 1 (Adaptee)

class WeatherAPI1 {
getWeatherData() {
// API-specific implementation and response format
return {
temperature: 25,
humidity: 70,
windSpeed: 12,
};
}
}

// External API 2 (Adaptee)
class WeatherAPI2 {
fetchData() {
// API-specific implementation and response format
return {
temp: 30,
humid: 60,
wind: 8,
};
}
}

// Unified Weather API (Target Interface)
class WeatherAPI {
constructor(adapter) {
this.adapter = adapter;
}

getWeather() {
const data = this.adapter.fetchData();
// Common data format for the application
return {
temperature: data.temp || data.temperature,
humidity: data.humid || data.humidity,
windSpeed: data.wind || data.windSpeed,
};
}
}

// Adapters for the external APIs
class API1Adapter {
constructor(api) {
this.api = api;
}

fetchData() {
const data = this.api.getWeatherData();
return {
temperature: data.temperature,
humidity: data.humidity,
windSpeed: data.windSpeed,
};
}
}

class API2Adapter {
constructor(api) {
this.api = api;
}

fetchData() {
const data = this.api.fetchData();
return {
temperature: data.temp,
humidity: data.humid,
windSpeed: data.wind,
};
}
}

// Usage
const api1 = new WeatherAPI1();
const api2 = new WeatherAPI2();

const api1Adapter = new API1Adapter(api1);
const api2Adapter = new API2Adapter(api2);

const weatherAPI = new WeatherAPI(api1Adapter);
console.log(weatherAPI.getWeather()); // Outputs weather data from API 1

weatherAPI.adapter = api2Adapter;
console.log(weatherAPI.getWeather()); // Outputs weather data from API 2

Bridge Design Pattern

 // Implementor: Device

class Device {
constructor() {}

isEnabled() {}
enable() {}
disable() {}
getVolume() {}
setVolume(percent) {}
getChannel() {}
setChannel(channel) {}
}

// Concrete Implementor: TV
class TVDevice extends Device {
constructor(name) {
super();
this.enabled = false;
this.volume = 50;
this.channel = 1;
this.name =name;
}

isEnabled() {
return this.enabled;
}

enable() {
this.enabled = true;
}

disable() {
this.enabled = false;
}

getVolume() {
return this.volume;
}

setVolume(percent) {
this.volume = percent;
}

getChannel() {
return this.channel;
}

setChannel(channel) {
this.channel = channel;
}
}

// Concrete Implementor: Radio
class RadioDevice extends Device {
constructor(name) {
super();
this.enabled = false;
this.volume = 30;
this.channel = 100;
this.name = name;
}

isEnabled() {
return this.enabled;
}

enable() {
this.enabled = true;
}

disable() {
this.enabled = false;
}

getVolume() {
return this.volume;
}

setVolume(percent) {
this.volume = percent;
}

getChannel() {
return this.channel;
}

setChannel(channel) {
this.channel = channel;
}
}

// Abstraction: Remote
class Remote {
constructor(device) {
this.device = device;
}

togglePower() {
if (this.device.isEnabled()) {
this.device.disable();
console.log(`on ${this.device.name} Power Down Called`)
} else {
this.device.enable();
console.log(`on ${this.device.name} Power Up Called`)
}
}

volumeUp() {
this.device.setVolume(this.device.getVolume() + 10);
console.log(`on ${this.device.name} volumeUp called`)
}

volumeDown() {
this.device.setVolume(this.device.getVolume() - 10);
console.log(`on ${this.device.name} volumeDown called`)
}

channelUp() {
this.device.setChannel(this.device.getChannel() + 1);
console.log(`on ${this.device.name} channelUp called`)
}

channelDown() {
this.device.setChannel(this.device.getChannel() - 1);
console.log(`on ${this.device.name} channelDown called`)
}
}

// Usage
const tv = new TVDevice('MyTv');
const tvRemote = new Remote(tv);

const radio = new RadioDevice('MyRadio');
const radioRemote = new Remote(radio);

tvRemote.togglePower();
tvRemote.volumeUp();
tvRemote.channelUp();

radioRemote.togglePower();
radioRemote.volumeUp();
radioRemote.channelUp();

Abstract Factory Design Pattern in javascript

 // Abstract Product A

class CPU {
constructor() {
this.architecture = '';
}

getInfo() {
return `CPU Architecture: ${this.architecture}`;
}
}

// Concrete Product A1
class IntelCPU extends CPU {
constructor() {
super();
this.architecture = 'x86';
}
}

// Concrete Product A2
class AMDCPU extends CPU {
constructor() {
super();
this.architecture = 'x86_64';
}
}

// Abstract Product B
class GPU {
constructor() {
this.vendor = '';
}

getInfo() {
return `GPU Vendor: ${this.vendor}`;
}
}

// Concrete Product B1
class NvidiaGPU extends GPU {
constructor() {
super();
this.vendor = 'Nvidia';
}
}

// Concrete Product B2
class AMGGPU extends GPU {
constructor() {
super();
this.vendor = 'AMD';
}
}

// Abstract Factory
class ComputerFactory {
createCPU() {}
createGPU() {}
}

// Concrete Factory 1
class HighPerformanceComputerFactory extends ComputerFactory {
createCPU() {
return new IntelCPU();
}

createGPU() {
return new NvidiaGPU();
}
}

// Concrete Factory 2
class BudgetComputerFactory extends ComputerFactory {
createCPU() {
return new AMDCPU();
}

createGPU() {
return new AMGGPU();
}
}

// Client Code
const highPerformanceFactory = new HighPerformanceComputerFactory();
const highPerformanceCPU = highPerformanceFactory.createCPU();
const highPerformanceGPU = highPerformanceFactory.createGPU();

console.log(highPerformanceCPU.getInfo()); // Output: CPU Architecture: x86
console.log(highPerformanceGPU.getInfo()); // Output: GPU Vendor: Nvidia

const budgetFactory = new BudgetComputerFactory();
const budgetCPU = budgetFactory.createCPU();
const budgetGPU = budgetFactory.createGPU();

console.log(budgetCPU.getInfo()); // Output: CPU Architecture: x86_64
console.log(budgetGPU.getInfo()); // Output: GPU Vendor: AMD

Factory Design Pattern in node.js

 class Product {

constructor(name, price) {
this.name = name;
this.price = price;
}

getInfo() {
return `Product: ${this.name}, Price: ${this.price}`;
}
}

class ConcreteProductA extends Product {
constructor(name, price) {
super(name, price);
}
}

class ConcreteProductB extends Product {
constructor(name, price) {
super(name, price);
}
}

class ProductFactory {
createProduct(type, name, price) {
switch (type) {
case 'A':
return new ConcreteProductA(name, price);
case 'B':
return new ConcreteProductB(name, price);
default:
throw new Error('Invalid product type');
}
}
}

const factory = new ProductFactory();

const productA = factory.createProduct('A', 'Product A', 100);
const productB = factory.createProduct('B', 'Product B', 200);

console.log(productA.getInfo()); // Output: Product: Product A, Price: 100
console.log(productB.getInfo()); // Output: Product: Product B, Price: 200

Mysql SingleTone class

inside MysqlSingleTon.js


 const mysql = require('mysql');


class MysqlConnection {
constructor() {
this.connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'sangram#81',
database: 'playground'
});

this.connection.connect((err) => {
if (err) {
console.error('Error connecting to MySQL:', err);
return;
}
console.log('Connected to MySQL');
});
}

query(sql, values) {
return new Promise((resolve, reject) => {
this.connection.query(sql, values, (err, results) => {
if (err) {
reject(err);
return;
}
resolve(results);
});
});
}

close() {
return new Promise((resolve, reject) => {
this.connection.end((err) => {
if (err) {
console.error('Error closing MySQL connection:', err);
reject(err);
return;
}
console.log('MySQL connection closed');
resolve();
});
});
}

}

class MySQLSingleton {
constructor() {
if (!MySQLSingleton.instance) {
MySQLSingleton.instance = new MysqlConnection()
}
}
getInstance() {
return MySQLSingleton.instance
}
}


const instance = new MySQLSingleton();
Object.freeze(instance);

module.exports = instance;



consuming class created

const instance = require('./MysqlSingleTon')

const connection = instance.getInstance()

connection.query("SELECT * FROM emp;")
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
})
.finally(() => {
connection.close()
.catch((error) => {
console.error(error);
});
});