Design Patterns & SOLID Principles
"Architecting Robust Applications"
Design Pattern কোনো কোড নয়, এটা হলো কোড লেখার ব্লু-প্রিন্ট বা সমাধান। সফটওয়্যার ইঞ্জিনিয়ারিংয়ে কিছু কমন সমস্যা বারবার ফিরে আসে। সেই সমস্যাগুলো স্মার্টলি সমাধান করার নামই ডিজাইন প্যাটার্ন।
আর SOLID প্রিন্সিপাল হলো ভালো কোড লেখার ৫টি সুবর্ণ নিয়ম।
⭐ ১. Singleton Pattern: "একটাই রাজা" 👑
সমস্যা: এমন কিছু অবজেক্ট আছে যা অ্যাপ্লিকেশনে মাত্র একবারই তৈরি হওয়া উচিত। যেমন: ডাটাবেস কানেকশন বা কনফিগারেশন ফাইল।
সমাধান: Singleton প্যাটার্ন নিশ্চিত করে যে, একটি ক্লাসের মাত্র একটিই ইনস্ট্যান্স থাকবে।
💻 Code Implementation
class Database {
constructor() {
if (Database.instance) {
return Database.instance;
}
this.connection = "Connected to MongoDB";
this.randomID = Math.random();
Database.instance = this;
}
getConnection() {
return this.connection;
}
}
const db1 = new Database();
const db2 = new Database();
console.log(db1 === db2); // true
console.log(db1.randomID === db2.randomID); // true
⭐ ২. Factory Pattern: "অবজেক্ট তৈরির মেশিন" 🏭
সমস্যা: বিভিন্ন ধরণের ইউজার আছে। প্রতিবার ম্যানুয়ালি new লেখা নোংরা কোড তৈরি করে।
সমাধান: একটা ফ্যাক্টরি ফাংশন বানাও, যে কন্ডিশন চেক করে সঠিক অবজেক্ট বানিয়ে দেবে।
💻 Code Implementation
class Admin {
constructor() { this.role = "Admin"; this.access = ["read", "write"]; }
}
class Customer {
constructor() { this.role = "Customer"; this.access = ["read"]; }
}
class UserFactory {
static createUser(type) {
switch (type) {
case 'admin': return new Admin();
case 'customer': return new Customer();
default: throw new Error("Unknown user type");
}
}
}
const user1 = UserFactory.createUser('admin');
console.log(user1.role); // "Admin"
⭐ ৩. Observer Pattern: "YouTube Subscribe" 🔔
সমস্যা: যখন একটা ডাটা চেঞ্জ হয়, তখন অনেকগুলো জায়গায় আপডেট দরকার হয়।
সমাধান: Observer প্যাটার্ন (Pub/Sub)। Subject ইভেন্ট ছড়ায়, Observer শুনে কাজ করে।
💻 Code Implementation
class YouTubeChannel {
constructor() {
this.subscribers = [];
}
subscribe(fn) {
this.subscribers.push(fn);
}
notify(videoTitle) {
this.subscribers.forEach(subscriber => subscriber(videoTitle));
}
}
const jsChannel = new YouTubeChannel();
jsChannel.subscribe(title => console.log(`User 1: New Video - ${title}`));
jsChannel.subscribe(title => console.log(`User 2: Notification - ${title}`));
jsChannel.notify("Advanced JS Tutorial");
Redux, DOM Event Listener এবং Node.js EventEmitter এই প্যাটার্নে কাজ করে।
⭐ ৪. Strategy Pattern: "গুগল ম্যাপস" 🗺️
সমস্যা: একই কাজ করার অনেকগুলো উপায় থাকে (যেমন পেমেন্ট)। if-else দিয়ে সব লজিক এক ক্লাসে লেখা কঠিন।
সমাধান: প্রতিটি উপায়কে আলাদা Strategy ক্লাসে ভাগ করো এবং রানটাইমে সিলেক্ট করো।
💻 Code Implementation
class Bkash {
pay(amount) { console.log(`Paid ${amount} via Bkash`); }
}
class Stripe {
pay(amount) { console.log(`Paid ${amount} via Stripe USD`); }
}
class Checkout {
constructor(paymentMethod) {
this.paymentMethod = paymentMethod;
}
setMethod(method) {
this.paymentMethod = method;
}
processOrder(amount) {
this.paymentMethod.pay(amount);
}
}
const payment = new Checkout(new Bkash());
payment.processOrder(500); // Bkash
payment.setMethod(new Stripe());
payment.processOrder(500); // Stripe
⭐ ৫. Module Pattern: "প্রাইভেসি রক্ষা" 🛡️
ES6 এর আগে জাভাস্ক্রিপ্টে private ভেরিয়েবল ছিল না। তখন Closure ব্যবহার করে ডেটা হাইড করা হতো।
💻 Code Implementation
const BankAccount = (function() {
let balance = 0;
return {
deposit(amount) {
balance += amount;
console.log(`Deposited: ${amount}`);
},
getBalance() {
return balance;
}
};
})();
BankAccount.deposit(100);
console.log(BankAccount.getBalance()); // 100
// console.log(BankAccount.balance); // undefined (Private!)
⭐ ৬. Dependency Injection (DI): "সরঞ্জাম ধার নেওয়া" 🧰
সমস্যা: ক্লাসের ভেতরেই new Service() কল করলে Tightly Coupled হয়। টেস্টিং কঠিন হয়।
সমাধান: বাইরে থেকে ডিপেন্ডেন্সি পাস করো।
❌ Without DI:
class App {
constructor() {
this.db = new MySQLDatabase(); // Fixed! Change করা যাবে না
}
}
✅ With DI:
class App {
constructor(database) {
this.db = database; // বাইরে থেকে যা দেবে
}
}
const myApp = new App(new MongoDatabase()); // Flexible
⭐ ৭. SOLID Principles (The Senior Constitution) 📜
S — Single Responsibility Principle (SRP)
একটি ক্লাসের বা ফাংশনের একটাই কাজ থাকা উচিত।
O — Open/Closed Principle
Extension এর জন্য ওপেন, Modification এর জন্য ক্লোজড।
L — Liskov Substitution Principle (LSP)
চাইল্ড ক্লাস অবশ্যই প্যারেন্ট ক্লাসের বিকল্প হতে হবে।
I — Interface Segregation Principle
ছোট ছোট স্পেসিফিক ইন্টারফেস ভালো।
D — Dependency Inversion Principle
High-level মডিউল Low-level মডিউলের ওপর সরাসরি নির্ভর করবে না।
⭐ ৮. Real-World Architecture: Notification System 🚀
// 1. Singleton (Config)
const AppConfig = {
apiKey: "XYZ_SECRET",
env: "production"
};
// 2. Strategy (Methods)
class EmailSender { send(msg) { console.log(`📧 Sending Email: ${msg}`); } }
class SMSSender { send(msg) { console.log(`📱 Sending SMS: ${msg}`); } }
// 3. Factory (Choice)
class NotifierFactory {
static getNotifier(type) {
return type === 'email' ? new EmailSender() : new SMSSender();
}
}
// 4. Observer (System)
class System {
constructor() { this.observers = []; }
subscribe(fn) { this.observers.push(fn); }
triggerEvent(msg) {
this.observers.forEach(fn => fn(msg));
}
}
// --- Usage ---
const mySystem = new System();
mySystem.subscribe((msg) => {
const sender = NotifierFactory.getNotifier('email');
sender.send(msg);
});
mySystem.triggerEvent("Welcome to Senior JS Course!");
🧠 ৯. Senior Interview Questions
Q1: Singleton Pattern এর সমস্যা কী?
উত্তর: এটি গ্লোবাল স্টেটের মতো আচরণ করে। টেস্টিংয়ের সময় একে মক (Mock) করা কঠিন হয়।
Q2: Redux কোন প্যাটার্ন ফলো করে?
উত্তর: Observer Pattern (State change হলে UI সাবস্ক্রাইব করে) এবং Singleton (পুরো অ্যাপে একটাই Store থাকে)।
Q3: Composition vs Inheritance - কোনটা প্রেফার করবে?
উত্তর: Composition। কারণ ইনহেরিটেন্স কোডকে Tightly Coupled করে।
🚀 অধ্যায় ২২ সারাংশ (Checklist)
- ✓Singleton: একটাই ইনস্ট্যান্স
- ✓Factory: অবজেক্ট বানানোর লজিক আলাদা করা
- ✓Observer: ইভেন্ট লিসেনিং সিস্টেম
- ✓Strategy: রানটাইমে অ্যালগরিদম বদলানো
- ✓SOLID: ক্লিন আর্কিটেকচারের ৫টি স্তম্ভ
Next Step: আমাদের আর্কিটেকচার শেখা শেষ। এখন কোড ক্লিন রাখার জন্য এবং ভুল এড়ানোর জন্য আমাদের কিছু নিয়ম মানতে হবে। পরের অধ্যায়: "Clean Code & Best Practices"।