Atraskite vertės objektus JavaScript moduliuose, kad kodas būtų tvirtas, prižiūrimas ir testuojamas. Sužinokite, kaip įdiegti nekintamas duomenų struktūras ir pagerinti duomenų vientisumą.
JavaScript modulio vertės objektas: nekintamų duomenų modeliavimas
Šiuolaikinėje JavaScript programavimo praktikoje duomenų vientisumo ir kodo palaikymo užtikrinimas yra svarbiausias prioritetas. Viena galinga technika tai pasiekti yra vertės objektų (angl. Value Objects) naudojimas modulariose JavaScript programose. Vertės objektai, ypač derinami su nekintamumu, siūlo tvirtą požiūrį į duomenų modeliavimą, kuris lemia švaresnį, labiau nuspėjamą ir lengviau testuojamą kodą.
Kas yra vertės objektas?
Vertės objektas yra mažas, paprastas objektas, atspindintis konceptualią vertę. Skirtingai nuo esybių (angl. entities), kurios apibrėžiamos pagal savo tapatybę, vertės objektai apibrėžiami pagal savo atributus. Du vertės objektai laikomi lygiais, jei jų atributai yra lygūs, neatsižvelgiant į jų objektų tapatybę. Dažni vertės objektų pavyzdžiai:
- Valiuta: Atspindi piniginę vertę (pvz., 10 USD, 5 EUR).
- Datų intervalas: Atspindi pradžios ir pabaigos datą.
- El. pašto adresas: Atspindi galiojantį el. pašto adresą.
- Pašto kodas: Atspindi galiojantį pašto kodą konkrečiam regionui. (pvz., 90210 JAV, SW1A 0AA JK, 10115 Vokietijoje, 〒100-0001 Japonijoje)
- Telefono numeris: Atspindi galiojantį telefono numerį.
- Koordinatės: Atspindi geografinę vietą (platuma ir ilguma).
Pagrindinės vertės objekto savybės yra:
- Nekintamumas: Sukūrus vertės objektą, jo būsena negali būti pakeista. Tai pašalina nenumatytų šalutinių poveikių riziką.
- Lygybė, pagrįsta verte: Du vertės objektai yra lygūs, jei jų vertės yra lygios, o ne jei jie yra tas pats objektas atmintyje.
- Inkapsuliacija: Vidinė vertės reprezentacija yra paslėpta, o prieiga suteikiama per metodus. Tai leidžia atlikti patvirtinimą ir užtikrinti vertės vientisumą.
Kodėl verta naudoti vertės objektus?
Vertės objektų naudojimas jūsų JavaScript programose suteikia keletą svarbių pranašumų:
- Pagerintas duomenų vientisumas: Vertės objektai gali užtikrinti apribojimus ir patvirtinimo taisykles kūrimo metu, garantuodami, kad bus naudojami tik galiojantys duomenys. Pavyzdžiui, `EmailAddress` vertės objektas gali patvirtinti, kad įvesties eilutė iš tiesų yra galiojančio el. pašto formato. Tai sumažina klaidų plitimo sistemoje tikimybę.
- Sumažinti šalutiniai poveikiai: Nekintamumas pašalina galimybę netyčia modifikuoti vertės objekto būseną, todėl kodas tampa labiau nuspėjamas ir patikimesnis.
- Supaprastintas testavimas: Kadangi vertės objektai yra nekintami ir jų lygybė pagrįsta verte, vienetų testavimas tampa daug lengvesnis. Galite tiesiog sukurti vertės objektus su žinomomis vertėmis ir palyginti juos su laukiamais rezultatais.
- Didesnis kodo aiškumas: Vertės objektai padaro jūsų kodą išraiškingesnį ir lengviau suprantamą, aiškiai atspindėdami srities koncepcijas. Vietoj to, kad perduotumėte neapdorotas eilutes ar skaičius, galite naudoti vertės objektus, tokius kaip `Currency` ar `PostalCode`, todėl jūsų kodo ketinimai tampa aiškesni.
- Pagerintas moduliškumas: Vertės objektai inkapsuliuoja specifinę logiką, susijusią su tam tikra verte, skatindami atsakomybių atskyrimą ir padarydami jūsų kodą moduliškesnį.
- Geresnis bendradarbiavimas: Standartinių vertės objektų naudojimas skatina bendrą supratimą tarp komandų. Pavyzdžiui, visi supranta, ką reiškia „Valiutos“ (Currency) objektas.
Vertės objektų diegimas JavaScript moduliuose
Panagrinėkime, kaip įdiegti vertės objektus JavaScript naudojant ES modulius, sutelkiant dėmesį į nekintamumą ir tinkamą inkapsuliaciją.
Pavyzdys: El. pašto adreso vertės objektas
Apsvarstykime paprastą `EmailAddress` vertės objektą. El. pašto formatui patvirtinti naudosime reguliariąją išraišką.
```javascript // email-address.js const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; class EmailAddress { constructor(value) { if (!EmailAddress.isValid(value)) { throw new Error('Invalid email address format.'); } // Privati savybė (naudojant uždarinį) let _value = value; this.getValue = () => _value; // Getteris // Apsaugo nuo modifikavimo iš išorės Object.freeze(this); } getValue() { return this.value; } toString() { return this.getValue(); } static isValid(value) { return EMAIL_REGEX.test(value); } equals(other) { if (!(other instanceof EmailAddress)) { return false; } return this.getValue() === other.getValue(); } } export default EmailAddress; ```Paaiškinimas:
- Modulio eksportavimas: Klasė `EmailAddress` yra eksportuojama kaip modulis, todėl ją galima pakartotinai naudoti įvairiose programos dalyse.
- Patvirtinimas (validacija): Konstruktorius patvirtina įvesties el. pašto adresą naudodamas reguliariąją išraišką (`EMAIL_REGEX`). Jei el. paštas neteisingas, jis išmeta klaidą. Tai užtikrina, kad kuriami tik galiojantys `EmailAddress` objektai.
- Nekintamumas: `Object.freeze(this)` apsaugo nuo bet kokių `EmailAddress` objekto pakeitimų po jo sukūrimo. Bandymas modifikuoti užšaldytą objektą sukels klaidą. Taip pat naudojame uždarinius (closures), kad paslėptume `_value` savybę, todėl jos neįmanoma tiesiogiai pasiekti iš klasės išorės.
- `getValue()` metodas: `getValue()` metodas suteikia kontroliuojamą prieigą prie pagrindinės el. pašto adreso vertės.
- `toString()` metodas: `toString()` metodas leidžia vertės objektą lengvai konvertuoti į eilutę.
- `isValid()` statinis metodas: Statinis `isValid()` metodas leidžia patikrinti, ar eilutė yra galiojantis el. pašto adresas, nesukuriant klasės egzemplioriaus.
- `equals()` metodas: `equals()` metodas palygina du `EmailAddress` objektus pagal jų vertes, užtikrindamas, kad lygybė būtų nustatoma pagal turinį, o ne pagal objekto tapatybę.
Naudojimo pavyzdys
```javascript // main.js import EmailAddress from './email-address.js'; try { const email1 = new EmailAddress('test@example.com'); const email2 = new EmailAddress('test@example.com'); const email3 = new EmailAddress('invalid-email'); // Tai sukels klaidą console.log(email1.getValue()); // Išvestis: test@example.com console.log(email1.toString()); // Išvestis: test@example.com console.log(email1.equals(email2)); // Išvestis: true // Bandymas modifikuoti email1 sukels klaidą (reikalingas griežtasis režimas) // email1.value = 'new-email@example.com'; // Klaida: Cannot assign to read only property 'value' of object '#Pademonstruoti pranašumai
Šis pavyzdys demonstruoja pagrindinius vertės objektų principus:
- Patvirtinimas: `EmailAddress` konstruktorius užtikrina el. pašto formato patvirtinimą.
- Nekintamumas: `Object.freeze()` iškvietimas apsaugo nuo modifikavimo.
- Verte pagrįsta lygybė: `equals()` metodas palygina el. pašto adresus pagal jų vertes.
Sudėtingesni aspektai
Typescript
Nors ankstesnis pavyzdys naudoja gryną JavaScript, TypeScript gali žymiai pagerinti vertės objektų kūrimą ir tvirtumą. TypeScript leidžia apibrėžti jūsų vertės objektų tipus, suteikiant kompiliavimo laiko tipų tikrinimą ir geresnį kodo palaikymą. Štai kaip galite įdiegti `EmailAddress` vertės objektą naudojant TypeScript:
```typescript // email-address.ts const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; class EmailAddress { private readonly value: string; constructor(value: string) { if (!EmailAddress.isValid(value)) { throw new Error('Invalid email address format.'); } this.value = value; Object.freeze(this); } getValue(): string { return this.value; } toString(): string { return this.value; } static isValid(value: string): boolean { return EMAIL_REGEX.test(value); } equals(other: EmailAddress): boolean { return this.value === other.getValue(); } } export default EmailAddress; ```Pagrindiniai patobulinimai su TypeScript:
- Tipų saugumas: Savybė `value` yra aiškiai apibrėžta kaip `string` tipo, o konstruktorius užtikrina, kad būtų perduodamos tik eilutės.
- Tik skaitymui skirtos savybės: Raktinis žodis `readonly` užtikrina, kad savybė `value` gali būti priskirta tik konstruktoriuje, taip dar labiau sustiprinant nekintamumą.
- Pagerintas kodo užbaigimas ir klaidų aptikimas: TypeScript suteikia geresnį kodo užbaigimą ir padeda aptikti su tipais susijusias klaidas kūrimo metu.
Funkcinio programavimo metodai
Taip pat galite įdiegti vertės objektus naudodami funkcinio programavimo principus. Šis požiūris dažnai apima funkcijų naudojimą nekintamoms duomenų struktūroms kurti ir jomis manipuliuoti.
```javascript // currency.js import { isNil, isNumber, isString } from 'lodash-es'; function Currency(amount, code) { if (!isNumber(amount)) { throw new Error('Amount must be a number'); } if (!isString(code) || code.length !== 3) { throw new Error('Code must be a 3-letter string'); } const _amount = amount; const _code = code.toUpperCase(); return Object.freeze({ getAmount: () => _amount, getCode: () => _code, toString: () => `${_code} ${_amount}`, equals: (other) => { if (isNil(other) || typeof other.getAmount !== 'function' || typeof other.getCode !== 'function') { return false; } return other.getAmount() === _amount && other.getCode() === _code; } }); } export default Currency; // Pavyzdys // const price = Currency(19.99, 'USD'); ```Paaiškinimas:
- Gamyklinė funkcija (angl. Factory Function): `Currency` funkcija veikia kaip gamykla, kurianti ir grąžinanti nekintamą objektą.
- Uždariniai (angl. Closures): Kintamieji `_amount` ir `_code` yra uždaryti funkcijos apimtyje, todėl jie yra privatūs ir nepasiekiami iš išorės.
- Nekintamumas: `Object.freeze()` užtikrina, kad grąžintas objektas negali būti modifikuotas.
Serializavimas ir deserializavimas
Dirbant su vertės objektais, ypač paskirstytose sistemose arba saugant duomenis, dažnai prireiks juos serializuoti (konvertuoti į eilutės formatą, pvz., JSON) ir deserializuoti (konvertuoti atgal iš eilutės formato į vertės objektą). Naudojant JSON serializavimą, paprastai gaunate neapdorotas vertes, kurios reprezentuoja vertės objektą (eilutės reprezentaciją, skaičiaus reprezentaciją ir t. t.).
Atliekant deserializavimą, užtikrinkite, kad visada iš naujo sukursite vertės objekto egzempliorių naudodami jo konstruktorių, kad būtų užtikrintas patvirtinimas ir nekintamumas.
```javascript // Serializavimas const email = new EmailAddress('test@example.com'); const emailJSON = JSON.stringify(email.getValue()); // Serializuojama pagrindinė vertė console.log(emailJSON); // Išvestis: "test@example.com" // Deserializavimas const deserializedEmail = new EmailAddress(JSON.parse(emailJSON)); // Iš naujo sukuriamas vertės objektas console.log(deserializedEmail.getValue()); // Išvestis: test@example.com ```Realaus pasaulio pavyzdžiai
Vertės objektai gali būti taikomi įvairiuose scenarijuose:
- Elektroninė komercija: Produktų kainų vaizdavimas naudojant `Currency` vertės objektą, užtikrinant nuoseklų valiutų tvarkymą. Produktų SKU patvirtinimas su `SKU` vertės objektu.
- Finansinės programos: Piniginių sumų ir sąskaitų numerių tvarkymas su `Money` ir `AccountNumber` vertės objektais, užtikrinant patvirtinimo taisykles ir išvengiant klaidų.
- Geografinės programos: Koordinačių vaizdavimas su `Coordinates` vertės objektu, užtikrinant, kad platumos ir ilgumos vertės būtų galiojančiuose diapazonuose. Šalių vaizdavimas su `CountryCode` vertės objektu (pvz., "US", "GB", "DE", "JP", "BR").
- Vartotojų valdymas: El. pašto adresų, telefono numerių ir pašto kodų patvirtinimas naudojant tam skirtus vertės objektus.
- Logistika: Pristatymo adresų tvarkymas su `Address` vertės objektu, užtikrinant, kad visi reikalingi laukai būtų pateikti ir galiojantys.
Nauda anapus kodo
- Geresnis bendradarbiavimas: Vertės objektai apibrėžia bendrą žodyną jūsų komandoje ir projekte. Kai visi supranta, ką reiškia `PostalCode` ar `PhoneNumber`, bendradarbiavimas žymiai pagerėja.
- Lengvesnis naujų narių įvedimas: Nauji komandos nariai gali greitai perprasti srities modelį, suprasdami kiekvieno vertės objekto paskirtį ir apribojimus.
- Sumažėjusi kognityvinė apkrova: Inkapsuliuodami sudėtingą logiką ir patvirtinimą vertės objektuose, jūs atlaisvinate programuotojus, kad jie galėtų susitelkti ties aukštesnio lygio verslo logika.
Geriausios vertės objektų praktikos
- Išlaikykite juos mažus ir koncentruotus: Vertės objektas turėtų atspindėti vieną, gerai apibrėžtą koncepciją.
- Užtikrinkite nekintamumą: Neleiskite modifikuoti vertės objekto būsenos po jo sukūrimo.
- Įdiekite verte pagrįstą lygybę: Užtikrinkite, kad du vertės objektai būtų laikomi lygiais, jei jų vertės yra lygios.
- Pateikite `toString()` metodą: Tai palengvina vertės objektų vaizdavimą eilutėmis registravimo ir derinimo tikslais.
- Rašykite išsamius vienetų testus: Kruopščiai testuokite savo vertės objektų patvirtinimą, lygybę ir nekintamumą.
- Naudokite prasmingus pavadinimus: Pasirinkite pavadinimus, kurie aiškiai atspindi koncepciją, kurią vertės objektas reprezentuoja (pvz., `EmailAddress`, `Currency`, `PostalCode`).
Išvada
Vertės objektai siūlo galingą būdą modeliuoti duomenis JavaScript programose. Priimdami nekintamumo, patvirtinimo ir verte pagrįstos lygybės principus, galite sukurti tvirtesnį, lengviau prižiūrimą ir testuojamą kodą. Nesvarbu, ar kuriate mažą interneto programą, ar didelio masto įmonės sistemą, vertės objektų įtraukimas į jūsų architektūrą gali žymiai pagerinti jūsų programinės įrangos kokybę ir patikimumą. Naudodami modulius šiems objektams tvarkyti ir eksportuoti, sukuriate labai pakartotinai naudojamus komponentus, kurie prisideda prie moduliškesnės ir gerai struktūrizuotos kodo bazės. Vertės objektų taikymas yra svarbus žingsnis link švaresnių, patikimesnių ir lengviau suprantamų JavaScript programų kūrimo pasaulinei auditorijai.