- Регистрация
- 1 Мар 2015
- Сообщения
- 1,481
- Баллы
- 155
Design patterns are proven solutions to common software design problems. Whether you're building frontends or architecting backend systems, understanding these patterns can make your code more modular, flexible, and maintainable. In this blog, we’ll explore six powerful design patterns using JavaScript, each with simple explanations and code examples.
Table of Contents
Useful when only one object should control shared resources, like configuration or logging.
JavaScript Example
class Singleton {
constructor() {
if (Singleton.instance) return Singleton.instance;
this.config = {};
Singleton.instance = this;
}
setConfig(key, value) {
this.config[key] = value;
}
getConfig(key) {
return this.config[key];
}
}
const a = new Singleton();
a.setConfig("theme", "dark");
const b = new Singleton();
console.log(b.getConfig("theme")); // "dark"
2. Factory Method Pattern
Use this when creating different types of similar objects based on a condition.
JavaScript Example
class Developer {
constructor(name) {
this.name = name;
}
work() {
console.log(`${this.name} is writing code`);
}
}
class Tester {
constructor(name) {
this.name = name;
}
work() {
console.log(`${this.name} is testing code`);
}
}
class EmployeeFactory {
static create(type, name) {
if (type === "developer") return new Developer(name);
if (type === "tester") return new Tester(name);
}
}
const emp = EmployeeFactory.create("developer", "Zeeshan");
emp.work(); // Zeeshan is writing code
3. Abstract Factory Pattern
Ensures consistency between related products (e.g., UI components for themes).
JavaScript Example
class LightButton {
render() {
console.log("Rendering light button");
}
}
class DarkButton {
render() {
console.log("Rendering dark button");
}
}
class LightTextbox {
render() {
console.log("Rendering light textbox");
}
}
class DarkTextbox {
render() {
console.log("Rendering dark textbox");
}
}
class LightUIFactory {
createButton() { return new LightButton(); }
createTextbox() { return new LightTextbox(); }
}
class DarkUIFactory {
createButton() { return new DarkButton(); }
createTextbox() { return new DarkTextbox(); }
}
const factory = new DarkUIFactory();
factory.createButton().render(); // Rendering dark button
factory.createTextbox().render(); // Rendering dark textbox
4. Observer Pattern
Ideal for pub-sub models, event systems, or reactive UIs.
JavaScript Example
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(fn => fn !== observer);
}
notify(data) {
this.observers.forEach(observer => observer(data));
}
}
const news = new Subject();
function subscriber1(msg) { console.log("Sub1:", msg); }
function subscriber2(msg) { console.log("Sub2:", msg); }
news.subscribe(subscriber1);
news.subscribe(subscriber2);
news.notify("New blog published!");
5. Decorator Pattern
Useful when you want to avoid subclass explosion and prefer flexible feature composition.
JavaScript Example
class Coffee {
cost() { return 5; }
}
function withMilk(coffee) {
const cost = coffee.cost();
coffee.cost = () => cost + 1.5;
return coffee;
}
function withSugar(coffee) {
const cost = coffee.cost();
coffee.cost = () => cost + 0.5;
return coffee;
}
let myCoffee = new Coffee();
myCoffee = withMilk(myCoffee);
myCoffee = withSugar(myCoffee);
console.log(myCoffee.cost()); // 7.0
6. Facade Pattern
Used to hide complex logic or dependencies behind a simple method call.
JavaScript Example
class CPU {
start() { console.log("CPU started"); }
}
class Memory {
load() { console.log("Memory loaded"); }
}
class HardDrive {
read() { console.log("Hard drive read"); }
}
class Computer {
constructor() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
start() {
this.cpu.start();
this.memory.load();
this.hardDrive.read();
console.log("Computer started successfully!");
}
}
const pc = new Computer();
pc.start();
Final Thoughts
Design patterns are not about memorizing definitions — they're about recognizing recurring problems and applying battle-tested solutions. Mastering these patterns in JavaScript makes your applications easier to scale and maintain.
More Details:
Get all articles related to system design
Hastag: SystemDesignWithZeeshanAli
Git:
Table of Contents
- Singleton Pattern
- Factory Method Pattern
- Abstract Factory Pattern
- Observer Pattern
- Decorator Pattern
- Facade Pattern
Use CaseEnsure a class has only one instance and provide a global access point.
Useful when only one object should control shared resources, like configuration or logging.
JavaScript Example
class Singleton {
constructor() {
if (Singleton.instance) return Singleton.instance;
this.config = {};
Singleton.instance = this;
}
setConfig(key, value) {
this.config[key] = value;
}
getConfig(key) {
return this.config[key];
}
}
const a = new Singleton();
a.setConfig("theme", "dark");
const b = new Singleton();
console.log(b.getConfig("theme")); // "dark"
2. Factory Method Pattern
Use CaseDefine an interface for creating objects, but let the subclass or logic decide which class to instantiate.
Use this when creating different types of similar objects based on a condition.
JavaScript Example
class Developer {
constructor(name) {
this.name = name;
}
work() {
console.log(`${this.name} is writing code`);
}
}
class Tester {
constructor(name) {
this.name = name;
}
work() {
console.log(`${this.name} is testing code`);
}
}
class EmployeeFactory {
static create(type, name) {
if (type === "developer") return new Developer(name);
if (type === "tester") return new Tester(name);
}
}
const emp = EmployeeFactory.create("developer", "Zeeshan");
emp.work(); // Zeeshan is writing code
3. Abstract Factory Pattern
Use CaseCreate families of related objects without specifying their concrete classes.
Ensures consistency between related products (e.g., UI components for themes).
JavaScript Example
class LightButton {
render() {
console.log("Rendering light button");
}
}
class DarkButton {
render() {
console.log("Rendering dark button");
}
}
class LightTextbox {
render() {
console.log("Rendering light textbox");
}
}
class DarkTextbox {
render() {
console.log("Rendering dark textbox");
}
}
class LightUIFactory {
createButton() { return new LightButton(); }
createTextbox() { return new LightTextbox(); }
}
class DarkUIFactory {
createButton() { return new DarkButton(); }
createTextbox() { return new DarkTextbox(); }
}
const factory = new DarkUIFactory();
factory.createButton().render(); // Rendering dark button
factory.createTextbox().render(); // Rendering dark textbox
4. Observer Pattern
Use CaseNotify multiple observers about state changes in another object.
Ideal for pub-sub models, event systems, or reactive UIs.
JavaScript Example
class Subject {
constructor() {
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
unsubscribe(observer) {
this.observers = this.observers.filter(fn => fn !== observer);
}
notify(data) {
this.observers.forEach(observer => observer(data));
}
}
const news = new Subject();
function subscriber1(msg) { console.log("Sub1:", msg); }
function subscriber2(msg) { console.log("Sub2:", msg); }
news.subscribe(subscriber1);
news.subscribe(subscriber2);
news.notify("New blog published!");
5. Decorator Pattern
Use CaseDynamically add functionality to objects without changing their structure.
Useful when you want to avoid subclass explosion and prefer flexible feature composition.
JavaScript Example
class Coffee {
cost() { return 5; }
}
function withMilk(coffee) {
const cost = coffee.cost();
coffee.cost = () => cost + 1.5;
return coffee;
}
function withSugar(coffee) {
const cost = coffee.cost();
coffee.cost = () => cost + 0.5;
return coffee;
}
let myCoffee = new Coffee();
myCoffee = withMilk(myCoffee);
myCoffee = withSugar(myCoffee);
console.log(myCoffee.cost()); // 7.0
6. Facade Pattern
Use CaseProvide a simple interface to a complex subsystem.
Used to hide complex logic or dependencies behind a simple method call.
JavaScript Example
class CPU {
start() { console.log("CPU started"); }
}
class Memory {
load() { console.log("Memory loaded"); }
}
class HardDrive {
read() { console.log("Hard drive read"); }
}
class Computer {
constructor() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
start() {
this.cpu.start();
this.memory.load();
this.hardDrive.read();
console.log("Computer started successfully!");
}
}
const pc = new Computer();
pc.start();
Final Thoughts
Design patterns are not about memorizing definitions — they're about recognizing recurring problems and applying battle-tested solutions. Mastering these patterns in JavaScript makes your applications easier to scale and maintain.
More Details:
Get all articles related to system design
Hastag: SystemDesignWithZeeshanAli
Git: