Atraskite pažangius JavaScript modulių šablonus, skirtus kurti sudėtingus objektus lanksčiai, prižiūrimai ir testuojamai. Susipažinkite su „Factory“, „Builder“ ir „Prototype“ šablonais su praktiniais pavyzdžiais.
JavaScript modulių kūrimo šablonai: sudėtingų objektų kūrimo įvaldymas
„JavaScript“ kalboje sudėtingų objektų kūrimas gali greitai tapti sudėtingas, o tai lemia kodą, kurį sunku prižiūrėti, testuoti ir plėsti. Modulių šablonai suteikia struktūrizuotą požiūrį į kodo organizavimą ir funkcionalumo inkapsuliavimą. Tarp šių šablonų „Factory“, „Builder“ ir „Prototype“ išsiskiria kaip galingi įrankiai sudėtingų objektų kūrimui valdyti. Šiame straipsnyje gilinamasi į šiuos šablonus, pateikiami praktiniai pavyzdžiai ir pabrėžiama jų nauda kuriant tvirtas ir keičiamo dydžio „JavaScript“ programas.
Objektų kūrimo šablonų poreikio supratimas
Tiesioginis sudėtingų objektų kūrimas naudojant konstruktorius gali sukelti keletą problemų:
- Glaudus susiejimas: Kliento kodas tampa glaudžiai susietas su konkrečia kuriamąja klase, todėl sunku pakeisti realizacijas ar įdiegti naujas variacijas.
- Kodo dubliavimas: Objektų kūrimo logika gali būti dubliuojama keliose kodo bazės dalyse, o tai padidina klaidų riziką ir apsunkina priežiūrą.
- Sudėtingumas: Pats konstruktorius gali tapti pernelyg sudėtingas, tvarkydamas daugybę parametrų ir inicializavimo žingsnių.
Objektų kūrimo šablonai sprendžia šias problemas abstrahuodami egzempliorių kūrimo procesą, skatindami laisvą susiejimą, mažindami kodo dubliavimą ir supaprastindami sudėtingų objektų kūrimą.
„Factory“ šablonas
„Factory“ šablonas suteikia centralizuotą būdą kurti skirtingų tipų objektus, nenurodant tikslios klasės, kurią reikia sukurti. Jis inkapsuliuoja objektų kūrimo logiką, leidžiančią kurti objektus pagal konkrečius kriterijus ar konfigūracijas. Tai skatina laisvą susiejimą ir palengvina perėjimą tarp skirtingų realizacijų.
„Factory“ šablonų tipai
Yra keletas „Factory“ šablono variantų, įskaitant:
- Paprastas fabrikas (Simple Factory): Viena gamyklos klasė, kuri kuria objektus pagal pateiktus duomenis.
- Gamyklinis metodas (Factory Method): Sąsaja arba abstrakti klasė, kuri apibrėžia metodą objektams kurti, leisdama poklasiams nuspręsti, kurią klasę sukurti.
- Abstraktus fabrikas (Abstract Factory): Sąsaja arba abstrakti klasė, kuri suteikia sąsają susijusių ar priklausomų objektų šeimoms kurti, nenurodant jų konkrečių klasių.
Paprasto fabriko pavyzdys
Panagrinėkime scenarijų, kai reikia sukurti skirtingų tipų vartotojų objektus (pvz., AdminUser, RegularUser, GuestUser) pagal jų vaidmenį.
// User classes
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';
}
}
// Simple 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');
}
}
}
// Usage
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);
Gamyklinio metodo pavyzdys
Dabar įgyvendinkime „Factory Method“ šabloną. Sukursime abstrakčią klasę gamyklai ir poklasius kiekvieno vartotojo tipo gamyklai.
// Abstract Factory
class UserFactory {
createUser(name) {
throw new Error('Method not implemented');
}
}
// Concrete Factories
class AdminUserFactory extends UserFactory {
createUser(name) {
return new AdminUser(name);
}
}
class RegularUserFactory extends UserFactory {
createUser(name) {
return new RegularUser(name);
}
}
// Usage
const adminFactory = new AdminUserFactory();
const regularFactory = new RegularUserFactory();
const admin = adminFactory.createUser('Alice');
const regular = regularFactory.createUser('Bob');
console.log(admin);
console.log(regular);
Abstrakaus fabriko pavyzdys
Sudėtingesniam scenarijui, apimančiam susijusių objektų šeimas, apsvarstykite „Abstract Factory“. Įsivaizduokime, kad reikia sukurti vartotojo sąsajos elementus skirtingoms operacinėms sistemoms (pvz., „Windows“, „macOS“). Kiekvienai OS reikalingas specifinis UI komponentų rinkinys (mygtukai, teksto laukai ir t. t.).
// Abstract Products
class Button {
render() {
throw new Error('Method not implemented');
}
}
class TextField {
render() {
throw new Error('Method not implemented');
}
}
// Concrete Products
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';
}
}
// Abstract Factory
class UIFactory {
createButton() {
throw new Error('Method not implemented');
}
createTextField() {
throw new Error('Method not implemented');
}
}
// Concrete Factories
class WindowsUIFactory extends UIFactory {
createButton() {
return new WindowsButton();
}
createTextField() {
return new WindowsTextField();
}
}
class macOSUIFactory extends UIFactory {
createButton() {
return new macOSButton();
}
createTextField() {
return new macOSTextField();
}
}
// Usage
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“ šablono privalumai
- Laisvas susiejimas: Atsieja kliento kodą nuo kuriamų konkrečių klasių.
- Inkapsuliacija: Inkapsuliuoja objektų kūrimo logiką vienoje vietoje.
- Lankstumas: Palengvina perėjimą tarp skirtingų realizacijų arba naujų objektų tipų pridėjimą.
- Testuojamumas: Supaprastina testavimą, leisdamas imituoti (mock) arba pakeisti (stub) gamyklą.
„Builder“ šablonas
„Builder“ šablonas ypač naudingas, kai reikia sukurti sudėtingus objektus su daugybe pasirenkamų parametrų ar konfigūracijų. Užuot perdavus visus šiuos parametrus konstruktoriui, „Builder“ šablonas leidžia konstruoti objektą žingsnis po žingsnio, suteikiant sklandžią sąsają kiekvienam parametrui nustatyti atskirai.
Kada naudoti „Builder“ šabloną
„Builder“ šablonas tinka scenarijams, kai:
- Objekto kūrimo procesas susideda iš kelių žingsnių.
- Objektas turi daug pasirenkamų parametrų.
- Norite suteikti aiškų ir skaitomą būdą objektui konfigūruoti.
„Builder“ šablono pavyzdys
Panagrinėkime scenarijų, kai reikia sukurti `Computer` objektą su įvairiais pasirenkamais komponentais (pvz., procesoriumi, RAM, atminties įrenginiu, vaizdo plokšte). „Builder“ šablonas gali padėti mums sukurti šį objektą struktūrizuotai ir skaitomai.
// Computer class
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 class
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);
}
}
// Usage
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“ šablono privalumai
- Geresnis skaitomumas: Suteikia sklandžią sąsają sudėtingiems objektams konfigūruoti, todėl kodas tampa skaitomesnis ir lengviau prižiūrimas.
- Sumažintas sudėtingumas: Supaprastina objektų kūrimo procesą, suskaidydamas jį į mažesnius, valdomus žingsnius.
- Lankstumas: Leidžia kurti skirtingas objekto variacijas, konfigūruojant skirtingus parametrų derinius.
- Apsaugo nuo konstruktorių „teleskopo“: Leidžia išvengti kelių konstruktorių su skirtingais parametrų sąrašais poreikio.
„Prototype“ šablonas
„Prototype“ šablonas leidžia kurti naujus objektus klonuojant esamą objektą, vadinamą prototipu. Tai ypač naudinga kuriant objektus, kurie yra panašūs vienas į kitą, arba kai objekto kūrimo procesas yra brangus.
Kada naudoti „Prototype“ šabloną
„Prototype“ šablonas tinka scenarijams, kai:
- Reikia sukurti daug objektų, kurie yra panašūs vienas į kitą.
- Objekto kūrimo procesas yra skaičiavimo požiūriu brangus.
- Norite išvengti poklasių kūrimo.
„Prototype“ šablono pavyzdys
Panagrinėkime scenarijų, kai reikia sukurti kelis `Shape` objektus su skirtingomis savybėmis (pvz., spalva, pozicija). Užuot kūrę kiekvieną objektą nuo nulio, galime sukurti prototipo figūrą ir ją klonuoti, kad sukurtume naujas figūras su pakeistomis savybėmis.
// Shape class
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);
}
}
// Usage
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(); // Original prototype remains unchanged
Giluminis klonavimas
Aukščiau pateiktas pavyzdys atlieka paviršutinišką kopiją. Objektams, kuriuose yra įdėtų objektų ar masyvų, reikės giluminio klonavimo mechanizmo, kad būtų išvengta nuorodų dalijimosi. Tokios bibliotekos kaip „Lodash“ suteikia giluminio klonavimo funkcijas, arba galite įgyvendinti savo rekursinę giluminio klonavimo funkciją.
// Deep clone function (using JSON stringify/parse)
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
// Example with nested object
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(); // Output: Drawing a circle with radius 5 and color blue
clonedCircle.draw(); // Output: Drawing a circle with radius 10 and color green
„Prototype“ šablono privalumai
- Sumažintos objektų kūrimo išlaidos: Kuria naujus objektus klonuojant esamus objektus, išvengiant brangių inicializavimo žingsnių.
- Supaprastintas objektų kūrimas: Supaprastina objektų kūrimo procesą, paslepiant objekto inicializavimo sudėtingumą.
- Dinamiškas objektų kūrimas: Leidžia dinamiškai kurti naujus objektus pagal esamus prototipus.
- Išvengiama poklasių kūrimo: Gali būti naudojamas kaip alternatyva poklasiams kuriant objektų variacijas.
Tinkamo šablono pasirinkimas
Pasirinkimas, kurį objektų kūrimo šabloną naudoti, priklauso nuo konkrečių jūsų programos reikalavimų. Štai trumpas vadovas:
- „Factory“ šablonas: Naudokite, kai reikia kurti skirtingų tipų objektus pagal konkrečius kriterijus ar konfigūracijas. Tinka, kai objektų kūrimas yra gana paprastas, bet turi būti atsietas nuo kliento.
- „Builder“ šablonas: Naudokite, kai reikia kurti sudėtingus objektus su daugybe pasirenkamų parametrų ar konfigūracijų. Geriausiai tinka, kai objekto konstravimas yra kelių žingsnių procesas.
- „Prototype“ šablonas: Naudokite, kai reikia sukurti daug objektų, kurie yra panašūs vienas į kitą, arba kai objekto kūrimo procesas yra brangus. Idealus kuriant esamų objektų kopijas, ypač jei klonavimas yra efektyvesnis nei kūrimas nuo nulio.
Realaus pasaulio pavyzdžiai
Šie šablonai plačiai naudojami daugelyje „JavaScript“ karkasų ir bibliotekų. Štai keletas realaus pasaulio pavyzdžių:
- „React“ komponentai: „Factory“ šablonas gali būti naudojamas kurti skirtingų tipų „React“ komponentus pagal `props` ar konfigūraciją.
- „Redux“ veiksmai: „Factory“ šablonas gali būti naudojamas kurti „Redux“ veiksmus su skirtingais duomenimis (`payloads`).
- Konfigūracijos objektai: „Builder“ šablonas gali būti naudojamas kurti sudėtingus konfigūracijos objektus su daugybe pasirenkamų nustatymų.
- Žaidimų kūrimas: „Prototype“ šablonas dažnai naudojamas žaidimų kūrime kuriant kelis žaidimo objektų egzempliorius (pvz., veikėjus, priešus) pagal prototipą.
Išvada
Objektų kūrimo šablonų, tokių kaip „Factory“, „Builder“ ir „Prototype“, įvaldymas yra būtinas kuriant tvirtas, prižiūrimas ir keičiamo dydžio „JavaScript“ programas. Suprasdami kiekvieno šablono stipriąsias ir silpnąsias puses, galite pasirinkti tinkamą įrankį darbui ir elegantiškai bei efektyviai kurti sudėtingus objektus. Šie šablonai skatina laisvą susiejimą, mažina kodo dubliavimą ir supaprastina objektų kūrimo procesą, todėl kodas tampa švaresnis, labiau testuojamas ir lengviau prižiūrimas. Apgalvotai taikydami šiuos šablonus, galite žymiai pagerinti bendrą savo „JavaScript“ projektų kokybę.