Design Patterns — Singleton, Factory, Observer, Strategy
Muammodan boshlaylik
To'lov funksiyasi:
function tolovQil(usul, summa) {
if (usul === 'click') {
// 30 qator Click logikasi
} else if (usul === 'payme') {
// 30 qator Payme logikasi
} else if (usul === 'uzum') {
// 30 qator Uzum logikasi
}
// yangi usul — yana bitta else if...
}
Har bir yangi to'lov usuli — eski funksiyani o'zgartirish va hamma narsani qayta test qilish. Bunday muammolar ming marta uchragan va ularning sinalgan yechimlari bor — design pattern lar.
Design pattern nima
Design pattern — tez-tez uchraydigan muammoning qayta ishlatiladigan yechim shabloni. Bu tayyor kod emas, balki g'oya: "bunday vaziyatda kodni shunday tuzing". 1994-yilda "Gang of Four" kitobida 23 ta pattern tasvirlangan; JavaScript da ularning ko'pi soddaroq ko'rinishga ega, chunki funksiyalar — first-class.
Bu darsda eng ko'p ishlatiladigan to'rttasi.
Singleton — yagona nusxa
Muammo: ilovada faqat bitta nusxa bo'lishi kerak: sozlamalar, API klient, ma'lumotlar bazasi ulanishi.
JavaScript da ES modul o'zi singleton — modul faqat bir marta bajariladi:
// src/api.js
class ApiKlient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.token = null;
}
async get(yol) {
const res = await fetch(this.baseUrl + yol, {
headers: this.token ? { Authorization: `Bearer ${this.token}` } : {},
});
return res.json();
}
}
export const api = new ApiKlient(import.meta.env.VITE_API_URL);
// istalgan fayl
import { api } from './api.js'; // har doim o'sha bitta nusxa
Klassik ko'rinish:
class Sozlamalar {
static #nusxa = null;
static olish() {
if (!Sozlamalar.#nusxa) Sozlamalar.#nusxa = new Sozlamalar();
return Sozlamalar.#nusxa;
}
}
Sozlamalar.olish() === Sozlamalar.olish(); // true
[!WARNING] Singleton — yashirin global holat. Uni testlashda almashtirish qiyin va hamma joydan o'zgartirilishi mumkin. Faqat haqiqatan yagona bo'lishi kerak bo'lgan resurslar uchun ishlating.
Factory — obyekt yaratishni markazlashtirish
Muammo: turga qarab har xil obyekt yaratish kerak, va bu mantiq kod bo'ylab tarqalib ketgan.
class Matn { constructor(o) { this.o = o; } chiz() { return `<input type="text" name="${this.o.nom}">`; } }
class Raqam { constructor(o) { this.o = o; } chiz() { return `<input type="number" name="${this.o.nom}">`; } }
class Tanlov {
constructor(o) { this.o = o; }
chiz() {
const opts = this.o.variantlar.map((v) => `<option>${v}</option>`).join('');
return `<select name="${this.o.nom}">${opts}</select>`;
}
}
const turlar = { matn: Matn, raqam: Raqam, tanlov: Tanlov };
function maydonYarat(sozlama) {
const Klass = turlar[sozlama.tur];
if (!Klass) throw new Error(`Noma'lum maydon turi: ${sozlama.tur}`);
return new Klass(sozlama);
}
const forma = [
{ tur: 'matn', nom: 'ism' },
{ tur: 'raqam', nom: 'yosh' },
{ tur: 'tanlov', nom: 'shahar', variantlar: ['Toshkent', 'Buxoro'] },
];
const html = forma.map((s) => maydonYarat(s).chiz()).join('');
Chaqiruvchi qaysi klass yaratilishini bilmaydi — faqat maydonYarat ni. Yangi tur — turlar ga bitta qator.
[!INFO] JavaScript da factory ko'pincha oddiy funksiya:
function createUser(ism) { return { ism, salom() {...} }; }—newva klasslarsiz obyekt yaratish ham "factory function" deyiladi.
Observer — obuna bo'lish
Muammo: bitta obyekt o'zgarganda boshqa ko'p qismlar xabardor bo'lishi kerak, lekin ular bir-biriga bog'lanmasligi kerak.
class EventEmitter {
#tinglovchilar = new Map();
on(hodisa, fn) {
if (!this.#tinglovchilar.has(hodisa)) this.#tinglovchilar.set(hodisa, new Set());
this.#tinglovchilar.get(hodisa).add(fn);
return () => this.off(hodisa, fn);
}
off(hodisa, fn) {
this.#tinglovchilar.get(hodisa)?.delete(fn);
}
emit(hodisa, ...args) {
this.#tinglovchilar.get(hodisa)?.forEach((fn) => fn(...args));
}
}
const hodisalar = new EventEmitter();
hodisalar.on('buyurtma:yaratildi', (b) => emailYubor(b.email));
hodisalar.on('buyurtma:yaratildi', (b) => analitika('purchase', b.summa));
hodisalar.on('buyurtma:yaratildi', () => savatniTozala());
// buyurtma moduli boshqalar haqida bilmaydi
hodisalar.emit('buyurtma:yaratildi', buyurtma);
Siz Observer ni har kuni ishlatasiz: addEventListener, Promise .then, 79-darsdagi store subscribe.
Strategy — almashtiriladigan algoritmlar
Muammo: bir vazifaning bir nechta usuli bor va ular if/else zanjirida.
Dars boshidagi to'lov misoli:
const tolovStrategiyalari = {
click: {
async tolov(summa) { /* Click API */ },
komissiya: (summa) => summa * 0.01,
},
payme: {
async tolov(summa) { /* Payme API */ },
komissiya: (summa) => summa * 0.015,
},
naqd: {
async tolov() { return { holat: 'kutilmoqda' }; },
komissiya: () => 0,
},
};
async function tolovQil(usul, summa) {
const strategiya = tolovStrategiyalari[usul];
if (!strategiya) throw new Error(`Qo'llab-quvvatlanmaydigan usul: ${usul}`);
const jami = summa + strategiya.komissiya(summa);
return strategiya.tolov(jami);
}
Yangi usul — yangi obyekt; tolovQil o'zgarmaydi. Boshqa misollar: tartiblash usullari, validatsiya qoidalari, narx hisoblash (chegirma turlari).
const tartiblar = {
arzon: (a, b) => a.narx - b.narx,
qimmat: (a, b) => b.narx - a.narx,
yangi: (a, b) => b.sana - a.sana,
};
mahsulotlar.toSorted(tartiblar[tanlangan]);
Qisqacha
| Pattern | Muammo | JS dagi ko'rinishi |
|---|---|---|
| Singleton | Yagona nusxa | Modul eksporti |
| Factory | Turga qarab yaratish | Funksiya + turlar xaritasi |
| Observer | O'zgarish haqida xabar | on/emit, addEventListener, subscribe |
| Strategy | Almashtiriladigan algoritm | Funksiyalar obyekti |
[!TIP] Pattern ni pattern uchun ishlatmang. Ikki shartli
if/else— Strategy talab qilmaydi. Pattern kod murakkablashib, takrorlanish yoki o'zgartirish qiyinligi sezilganda kerak.
Nima uchun shunday?
Nima uchun pattern larni o'rganish kerak — har bir muammoni o'zim hal qila olmaymanmi?
Hal qila olasiz, lekin pattern lar ikki narsa beradi. Birinchidan, sinalgan yechim: minglab dasturchilar bu muammolarga duch kelgan va bu yechimlarning qayerda ishlashi va qayerda muammo tug'dirishi ma'lum. Ikkinchidan, umumiy til: "bu yerda Strategy ishlatamiz" degan bitta jumla jamoadoshga butun arxitekturani tushuntiradi. Pattern larning umumiy g'oyasi — o'zgaradigan narsani o'zgarmaydigandan ajratish: yangi to'lov usuli, yangi maydon turi yoki yangi tinglovchi qo'shilganda eski, sinalgan kod tegilmay qoladi.
Xulosa
Eslab qoling:
- Design pattern — tez-tez uchraydigan muammoning sinalgan yechim shabloni
- Singleton — yagona nusxa; JS da modul eksporti; ehtiyotkorlik bilan
- Factory — yaratish mantiqini bir joyga yig'ish; turlar xaritasi
- Observer —
on/off/emit; obunachilar bir-biriga bog'lanmaydi - Strategy —
if/elseo'rniga almashtiriladigan funksiyalar obyekti - Pattern ni faqat haqiqiy muammo bo'lganda ishlating
Keyingi dars
81-dars: Testing — Jest bilan JavaScript testlash