Murakkab obyektlarni yaratish uchun JavaScript modul patternlarini o'rganing. Moslashuvchanlik va testlanuvchanlik uchun Factory, Builder va Prototype patternlarini amaliy misollar bilan ko'rib chiqing.
JavaScript Modul Quruvchi Patternlari: Murakkab Obyektlarni Yaratishni O'zlashtirish
JavaScriptda murakkab obyektlarni yaratish tezda qiyinlashib, qo‘llab-quvvatlash, sinovdan o‘tkazish va kengaytirish qiyin bo‘lgan kodga olib kelishi mumkin. Modul patternlari kodni tashkil etish va funksionallikni inkapsulyatsiya qilish uchun tizimli yondashuvni taqdim etadi. Ushbu patternlar orasida Factory, Builder va Prototype patternlari murakkab obyektlarni yaratishni boshqarish uchun kuchli vositalar sifatida ajralib turadi. Ushbu maqolada bu patternlar chuqur o‘rganilib, amaliy misollar keltiriladi va ularning mustahkam va kengaytiriladigan JavaScript ilovalarini yaratishdagi afzalliklari yoritiladi.
Obyekt Yaratish Patternlariga Bo'lgan Ehtiyojni Tushunish
Murakkab obyektlarni konstruktorlar yordamida to'g'ridan-to'g'ri yaratish bir nechta muammolarga olib kelishi mumkin:
- Qattiq bog'liqlik: Mijoz kodi instansiya qilinayotgan maxsus klassga qattiq bog'lanib qoladi, bu esa implementatsiyalarni almashtirish yoki yangi variantlarni kiritishni qiyinlashtiradi.
- Kodning takrorlanishi: Obyekt yaratish mantig'i kod bazasining bir nechta qismida takrorlanishi mumkin, bu xatolar xavfini oshiradi va qo'llab-quvvatlashni qiyinlashtiradi.
- Murakkablik: Konstruktorning o'zi haddan tashqari murakkablashib, ko'plab parametrlarni va ishga tushirish qadamlarini boshqarishi mumkin.
Obyekt yaratish patternlari bu muammolarni instansiyalash jarayonini abstraktlashtirish, bo'sh bog'liqlikni rag'batlantirish, kodning takrorlanishini kamaytirish va murakkab obyektlarni yaratishni soddalashtirish orqali hal qiladi.
Factory Patterni
Factory patterni turli xil turdagi obyektlarni yaratish uchun markazlashtirilgan usulni taqdim etadi, bunda instansiya qilinadigan aniq klass ko'rsatilmaydi. U obyekt yaratish mantig'ini inkapsulyatsiya qiladi, bu sizga ma'lum mezonlar yoki konfiguratsiyalarga asoslangan obyektlarni yaratish imkonini beradi. Bu bo'sh bog'liqlikni rag'batlantiradi va turli xil implementatsiyalar o'rtasida almashishni osonlashtiradi.
Factory Patternining Turlari
Factory patternining bir nechta turlari mavjud, jumladan:
- Oddiy Factory (Simple Factory): Berilgan ma'lumotlarga asoslanib obyektlar yaratadigan bitta factory klassi.
- Factory Metodi (Factory Method): Obyektlarni yaratish usulini belgilaydigan interfeys yoki abstrakt klass, bu quyi klasslarga qaysi klassni instansiya qilishni hal qilish imkonini beradi.
- Abstrakt Factory (Abstract Factory): Bog'liq yoki o'zaro bog'liq obyektlar oilalarini ularning aniq klasslarini ko'rsatmasdan yaratish uchun interfeysni ta'minlaydigan interfeys yoki abstrakt klass.
Oddiy Factory Misoli
Keling, rollariga qarab har xil turdagi foydalanuvchi obyektlarini (masalan, AdminUser, RegularUser, GuestUser) yaratishimiz kerak bo'lgan stsenariyni ko'rib chiqaylik.
// Foydalanuvchi klasslari
class AdminUser {
constructor(name) {
this.name = name;
this.role = 'admin';
}
}
class RegularUser {
constructor(name) {
this.name = name;
this.role = 'regular';
}
}
class GuestUser {
constructor() {
this.name = 'Guest';
this.role = 'guest';
}
}
// Oddiy Factory
class UserFactory {
static createUser(role, name) {
switch (role) {
case 'admin':
return new AdminUser(name);
case 'regular':
return new RegularUser(name);
case 'guest':
return new GuestUser();
default:
throw new Error('Invalid user role');
}
}
}
// Foydalanish
const admin = UserFactory.createUser('admin', 'Alice');
const regular = UserFactory.createUser('regular', 'Bob');
const guest = UserFactory.createUser('guest');
console.log(admin);
console.log(regular);
console.log(guest);
Factory Metodi Misoli
Endi, Factory Method patternini amalga oshiraylik. Biz factory uchun abstrakt klass va har bir foydalanuvchi turi uchun quyi klasslar yaratamiz.
// Abstrakt Factory
class UserFactory {
createUser(name) {
throw new Error('Method not implemented');
}
}
// Konkret Factory'lar
class AdminUserFactory extends UserFactory {
createUser(name) {
return new AdminUser(name);
}
}
class RegularUserFactory extends UserFactory {
createUser(name) {
return new RegularUser(name);
}
}
// Foydalanish
const adminFactory = new AdminUserFactory();
const regularFactory = new RegularUserFactory();
const admin = adminFactory.createUser('Alice');
const regular = regularFactory.createUser('Bob');
console.log(admin);
console.log(regular);
Abstrakt Factory Misoli
Bog'liq obyektlar oilalarini o'z ichiga olgan murakkabroq stsenariy uchun Abstrakt Factory'ni ko'rib chiqing. Tasavvur qiling, biz turli operatsion tizimlar (masalan, Windows, macOS) uchun UI elementlarini yaratishimiz kerak. Har bir OT maxsus UI komponentlari to'plamini (tugmalar, matn maydonlari va hokazo) talab qiladi.
// Abstrakt Mahsulotlar
class Button {
render() {
throw new Error('Method not implemented');
}
}
class TextField {
render() {
throw new Error('Method not implemented');
}
}
// Konkret Mahsulotlar
class WindowsButton extends Button {
render() {
return 'Windows Button';
}
}
class macOSButton extends Button {
render() {
return 'macOS Button';
}
}
class WindowsTextField extends TextField {
render() {
return 'Windows TextField';
}
}
class macOSTextField extends TextField {
render() {
return 'macOS TextField';
}
}
// Abstrakt Factory
class UIFactory {
createButton() {
throw new Error('Method not implemented');
}
createTextField() {
throw new Error('Method not implemented');
}
}
// Konkret Factory'lar
class WindowsUIFactory extends UIFactory {
createButton() {
return new WindowsButton();
}
createTextField() {
return new WindowsTextField();
}
}
class macOSUIFactory extends UIFactory {
createButton() {
return new macOSButton();
}
createTextField() {
return new macOSTextField();
}
}
// Foydalanish
function createUI(factory) {
const button = factory.createButton();
const textField = factory.createTextField();
return {
button: button.render(),
textField: textField.render()
};
}
const windowsUI = createUI(new WindowsUIFactory());
const macOSUI = createUI(new macOSUIFactory());
console.log(windowsUI);
console.log(macOSUI);
Factory Patternining Afzalliklari
- Bo'sh bog'liqlik: Mijoz kodini instansiya qilinayotgan konkret klasslardan ajratadi.
- Inkapsulyatsiya: Obyekt yaratish mantig'ini bitta joyga jamlaydi.
- Moslashuvchanlik: Turli xil implementatsiyalar o'rtasida almashishni yoki yangi turdagi obyektlarni qo'shishni osonlashtiradi.
- Testlanuvchanlik: Factory'ni mock yoki stub qilishga imkon berib, testlashni soddalashtiradi.
Builder Patterni
Builder patterni ayniqsa ko'p sonli ixtiyoriy parametrlar yoki konfiguratsiyalarga ega bo'lgan murakkab obyektlarni yaratish kerak bo'lganda foydalidir. Bu parametrlarning barchasini konstruktorga uzatish o'rniga, Builder patterni obyektni bosqichma-bosqich qurishga imkon beradi va har bir parametrni alohida o'rnatish uchun ravon interfeysni taqdim etadi.
Builder Patternidan Qachon Foydalanish Kerak
Builder patterni quyidagi stsenariylar uchun mos keladi:
- Obyekt yaratish jarayoni bir necha bosqichdan iborat bo'lsa.
- Obyekt ko'p sonli ixtiyoriy parametrlarga ega bo'lsa.
- Obyektni sozlash uchun aniq va o'qilishi oson usulni taqdim etishni xohlasangiz.
Builder Patterni Misoli
Keling, turli xil ixtiyoriy komponentlarga (masalan, CPU, RAM, xotira, videokarta) ega bo'lgan `Computer` obyektini yaratishimiz kerak bo'lgan stsenariyni ko'rib chiqaylik. Builder patterni bizga bu obyektni tizimli va o'qilishi oson usulda yaratishga yordam beradi.
// Computer klassi
class Computer {
constructor(cpu, ram, storage, graphicsCard, monitor) {
this.cpu = cpu;
this.ram = ram;
this.storage = storage;
this.graphicsCard = graphicsCard;
this.monitor = monitor;
}
toString() {
return `Computer: CPU=${this.cpu}, RAM=${this.ram}, Storage=${this.storage}, GraphicsCard=${this.graphicsCard}, Monitor=${this.monitor}`;
}
}
// Builder klassi
class ComputerBuilder {
constructor() {
this.cpu = null;
this.ram = null;
this.storage = null;
this.graphicsCard = null;
this.monitor = null;
}
setCPU(cpu) {
this.cpu = cpu;
return this;
}
setRAM(ram) {
this.ram = ram;
return this;
}
setStorage(storage) {
this.storage = storage;
return this;
}
setGraphicsCard(graphicsCard) {
this.graphicsCard = graphicsCard;
return this;
}
setMonitor(monitor) {
this.monitor = monitor;
return this;
}
build() {
return new Computer(this.cpu, this.ram, this.storage, this.graphicsCard, this.monitor);
}
}
// Foydalanish
const builder = new ComputerBuilder();
const myComputer = builder
.setCPU('Intel i7')
.setRAM('16GB')
.setStorage('1TB SSD')
.setGraphicsCard('Nvidia RTX 3080')
.setMonitor('32-inch 4K')
.build();
console.log(myComputer.toString());
const basicComputer = new ComputerBuilder()
.setCPU("Intel i3")
.setRAM("8GB")
.setStorage("500GB HDD")
.build();
console.log(basicComputer.toString());
Builder Patternining Afzalliklari
- O'qilishi osonligi: Murakkab obyektlarni sozlash uchun ravon interfeysni taqdim etadi, bu esa kodni o'qilishi osonroq va qo'llab-quvvatlanadigan qiladi.
- Murakkablikning kamayishi: Obyekt yaratish jarayonini kichikroq, boshqariladigan bosqichlarga bo'lish orqali soddalashtiradi.
- Moslashuvchanlik: Turli xil parametrlar kombinatsiyasini sozlash orqali obyektning turli xil variantlarini yaratishga imkon beradi.
- "Teleskopik" konstruktorlarning oldini oladi: Turli parametrlar ro'yxatiga ega bo'lgan bir nechta konstruktorlarga bo'lgan ehtiyojni bartaraf etadi.
Prototype Patterni
Prototype patterni sizga prototip deb nomlanuvchi mavjud obyektni klonlash orqali yangi obyektlar yaratish imkonini beradi. Bu, ayniqsa, bir-biriga o'xshash obyektlarni yaratishda yoki obyekt yaratish jarayoni qimmat bo'lganda foydalidir.
Prototype Patternidan Qachon Foydalanish Kerak
Prototype patterni quyidagi stsenariylar uchun mos keladi:
- Siz bir-biriga o'xshash ko'plab obyektlarni yaratishingiz kerak bo'lsa.
- Obyekt yaratish jarayoni hisoblash jihatidan qimmat bo'lsa.
- Siz quyi klasslarni yaratishdan qochishni xohlasangiz.
Prototype Patterni Misoli
Keling, turli xususiyatlarga (masalan, rang, pozitsiya) ega bo'lgan bir nechta `Shape` obyektlarini yaratishimiz kerak bo'lgan stsenariyni ko'rib chiqaylik. Har bir obyektni noldan yaratish o'rniga, biz prototip shaklni yaratib, uni o'zgartirilgan xususiyatlarga ega yangi shakllar yaratish uchun klonlashimiz mumkin.
// Shape klassi
class Shape {
constructor(color = 'red', x = 0, y = 0) {
this.color = color;
this.x = x;
this.y = y;
}
draw() {
console.log(`Drawing shape at (${this.x}, ${this.y}) with color ${this.color}`);
}
clone() {
return Object.assign(Object.create(Object.getPrototypeOf(this)), this);
}
}
// Foydalanish
const prototypeShape = new Shape();
const shape1 = prototypeShape.clone();
shape1.x = 10;
shape1.y = 20;
shape1.color = 'blue';
shape1.draw();
const shape2 = prototypeShape.clone();
shape2.x = 30;
shape2.y = 40;
shape2.color = 'green';
shape2.draw();
prototypeShape.draw(); // Asl prototip o'zgarishsiz qoladi
Chuqur Klonlash (Deep Cloning)
Yuqoridagi misol sayoz nusxalashni (shallow copy) amalga oshiradi. Ichki obyektlar yoki massivlarni o'z ichiga olgan obyektlar uchun havolalarni birgalikda ishlatmaslik uchun chuqur klonlash mexanizmi kerak bo'ladi. Lodash kabi kutubxonalar chuqur klonlash funksiyalarini taqdim etadi, yoki o'zingizning rekursiv chuqur klonlash funksiyangizni amalga oshirishingiz mumkin.
// Chuqur klonlash funksiyasi (JSON stringify/parse yordamida)
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
// Ichki obyektli misol
class Circle {
constructor(radius, style = { color: 'red' }) {
this.radius = radius;
this.style = style;
}
clone() {
return deepClone(this);
}
draw() {
console.log(`Drawing a circle with radius ${this.radius} and color ${this.style.color}`);
}
}
const originalCircle = new Circle(5, { color: 'blue' });
const clonedCircle = originalCircle.clone();
clonedCircle.radius = 10;
clonedCircle.style.color = 'green';
originalCircle.draw(); // Chiqish: Radiusi 5 va rangi ko'k bo'lgan doira chizilmoqda
clonedCircle.draw(); // Chiqish: Radiusi 10 va rangi yashil bo'lgan doira chizilmoqda
Prototype Patternining Afzalliklari
- Obyekt Yaratish Xarajatlarining Kamayishi: Mavjud obyektlarni klonlash orqali yangi obyektlar yaratadi, qimmat ishga tushirish bosqichlarini chetlab o'tadi.
- Soddalashtirilgan Obyekt Yaratish: Obyektni ishga tushirish murakkabligini yashirish orqali obyekt yaratish jarayonini soddalashtiradi.
- Dinamik Obyekt Yaratish: Mavjud prototiplarga asoslanib dinamik ravishda yangi obyektlar yaratishga imkon beradi.
- Quyi Klasslardan Qochish: Obyektlarning variantlarini yaratish uchun quyi klasslarga alternativa sifatida ishlatilishi mumkin.
To'g'ri Patternni Tanlash
Qaysi obyekt yaratish patternidan foydalanishni tanlash ilovangizning maxsus talablariga bog'liq. Mana qisqa qo'llanma:
- Factory Patterni: Ma'lum mezonlar yoki konfiguratsiyalarga asoslanib turli xil turdagi obyektlarni yaratishingiz kerak bo'lganda foydalaning. Obyekt yaratish nisbatan sodda, lekin mijozdan ajratilishi kerak bo'lganda yaxshi ishlaydi.
- Builder Patterni: Ko'p sonli ixtiyoriy parametrlar yoki konfiguratsiyalarga ega bo'lgan murakkab obyektlarni yaratishingiz kerak bo'lganda foydalaning. Obyekt qurilishi ko'p bosqichli jarayon bo'lganda eng yaxshisidir.
- Prototype Patterni: Bir-biriga o'xshash ko'plab obyektlarni yaratishingiz kerak bo'lganda yoki obyekt yaratish jarayoni qimmat bo'lganda foydalaning. Mavjud obyektlarning nusxalarini yaratish uchun ideal, ayniqsa klonlash noldan yaratishdan samaraliroq bo'lsa.
Haqiqiy Hayotdagi Misollar
Ushbu patternlar ko'plab JavaScript freymvorklari va kutubxonalarida keng qo'llaniladi. Mana bir nechta haqiqiy hayotdagi misollar:
- React Komponentlari: Factory patterni props yoki konfiguratsiyaga asoslangan holda har xil turdagi React komponentlarini yaratish uchun ishlatilishi mumkin.
- Redux Action'lari: Factory patterni turli xil payload'larga ega Redux action'larini yaratish uchun ishlatilishi mumkin.
- Konfiguratsiya Obyektlari: Builder patterni ko'p sonli ixtiyoriy sozlamalarga ega bo'lgan murakkab konfiguratsiya obyektlarini yaratish uchun ishlatilishi mumkin.
- O'yin Yaratish: Prototype patterni o'yin yaratishda prototip asosida o'yin obyektlarining (masalan, personajlar, dushmanlar) ko'plab nusxalarini yaratish uchun tez-tez ishlatiladi.
Xulosa
Factory, Builder va Prototype kabi obyekt yaratish patternlarini o'zlashtirish mustahkam, qo'llab-quvvatlanadigan va kengaytiriladigan JavaScript ilovalarini yaratish uchun juda muhimdir. Har bir patternning kuchli va zaif tomonlarini tushunib, siz vazifa uchun to'g'ri vositani tanlashingiz va murakkab obyektlarni nafislik va samaradorlik bilan yaratishingiz mumkin. Bu patternlar bo'sh bog'liqlikni rag'batlantiradi, kodning takrorlanishini kamaytiradi va obyekt yaratish jarayonini soddalashtiradi, bu esa toza, testlanuvchan va qo'llab-quvvatlanadigan kodga olib keladi. Ushbu patternlarni o'ylab qo'llash orqali siz JavaScript loyihalaringizning umumiy sifatini sezilarli darajada oshirishingiz mumkin.