JavaScript Design Patterns: The Observer Pattern

I JavaScript er det et problem som kommer opp ofte. Du trenger en måte å oppdatere deler av en side som svar på visse hendelser, med dataene disse gir. Si for eksempel brukerinngang som du deretter projiserer inn i en eller flere komponenter. Dette fører til mye push-and-pull i koden for å holde alt synkronisert.

det er her observatørdesignmønsteret kan hjelpe — det muliggjør en-til-mange databindinger mellom elementer. Denne enveis databindingen kan være hendelsesdrevet. Med dette mønsteret kan du bygge gjenbrukbar kode som løser for dine spesifikke behov.

I denne artikkelen vil jeg gjerne utforske observatørens designmønster. Det vil hjelpe deg med å løse et vanlig problem du ser i klientsiden scripting. Det er en-til-mange, enveis og hendelsesdrevet databinding. Det er et problem som kommer opp ofte når du har mange elementer som må synkroniseres.

jeg bruker ECMAScript 6 for å illustrere mønsteret. Ja, det vil være klasser, pilfunksjoner og konstanter. Føl deg fri til å utforske disse emnene på egen hånd hvis du ikke allerede er kjent. Jeg bruker deler AV ES6 som bare introduserer syntaktisk sukker, så det er bærbart MED ES5 om nødvendig.

Og jeg bruker Testdrevet Utvikling (Tdd) til å jobbe med mønsteret. På denne måten har du en måte å vite hvordan hver komponent er nyttig.

de nye språkfunksjonene I ES6 gir litt kortfattet kode. Så, la oss komme i gang.

Hendelsesobservatøren

en høynivåvisning av mønsteret ser slik ut:

EventObserver│ ├── subscribe: adds new observable events│ ├── unsubscribe: removes observable events|└── broadcast: executes all events with bound data

Etter at jeg har lest observatørmønsteret, legger jeg til et ordtall som bruker det. Ordet teller komponent vil ta denne observatøren og bringe det hele sammen.

for å initialisere EventObserver gjør:

class EventObserver { constructor() { this.observers = ; }}

Start med en tom liste over observerte hendelser, og gjør dette for hver ny forekomst. Fra nå av, la oss legge til flere metoder inne EventObserver for å kutte ut designmønsteret.

Abonner Metode

for å legge til nye hendelser gjør:

subscribe(fn) { this.observers.push(fn);}

Ta tak i listen over observerte hendelser og skyv et nytt element til matrisen. Listen over hendelser er en liste over tilbakeringingsfunksjoner.

En måte å teste denne metoden på i Vanlig JavaScript er som følger:

// Arrangeconst observer = new EventObserver();const fn = () => {};// Actobserver.subscribe(fn);// Assertassert.strictEqual(observer.observers.length, 1);

Jeg bruker Node påstander for å teste denne komponenten I Node. Nøyaktig samme påstander eksisterer Som Chai påstander også.

Merk listen over observerte hendelser består av ydmyke tilbakeringinger. Vi sjekker lengden på listen og hevder at tilbakeringingen er på listen.

Unsubscribe-Metoden

for å fjerne hendelser gjør:

unsubscribe(fn) { this.observers = this.observers.filter((subscriber) => subscriber !== fn);}

Filtrere ut fra listen uansett matcher tilbakeringing funksjon. Hvis det ikke er noen kamp, tilbakeringing blir å bo på listen. Filteret returnerer en ny liste og tilordner listen over observatører på nytt.

for å teste denne fine metoden, gjør:

// Arrangeconst observer = new EventObserver();const fn = () => {};observer.subscribe(fn);// Actobserver.unsubscribe(fn);// Assertassert.strictEqual(observer.observers.length, 0);

tilbakeringingen må samsvare med samme funksjon som er på listen. Hvis det er en kamp, fjerner avmeldingsmetoden den fra listen. Testen bruker funksjonsreferansen til å legge til og fjerne den.

Kringkastingsmetoden

for å ringe alle hendelser gjør:

broadcast(data) { this.observers.forEach((subscriber) => subscriber(data));}

Dette iterates gjennom listen over observerte hendelser og utfører alle tilbakeringinger. Med dette får du det nødvendige en-til-mange-forholdet til de abonnerte hendelsene. Du passerer i parameteren data som gjør tilbakeringing data bundet.

ES6 gjør koden mer effektiv med en pilfunksjon. Legg merke til funksjonen (subscriber) => subscriber(data) som gjør det meste av arbeidet. Denne one-liner pil funksjonen drar nytte AV denne korte ES6 syntaks. Dette er en klar forbedring I JavaScript programmeringsspråk.

for å teste denne kringkastingsmetoden, gjør:

// Arrangeconst observer = new EventObserver();let subscriberHasBeenCalled = false;const fn = (data) => subscriberHasBeenCalled = data;observer.subscribe(fn);// Actobserver.broadcast(true);// Assertassert(subscriberHasBeenCalled);

Bruk let i stedet for en const slik at vi kan endre verdien av variabelen. Dette gjør variabelen foranderlig som tillater meg å tilordne sin verdi inne i tilbakeringing. Ved å bruke en let i koden din, sendes et signal til andre programmerere at variabelen endrer seg på et tidspunkt. Dette gir lesbarhet og klarhet Til JavaScript-koden din.

denne testen gir meg den tilliten som er nødvendig for å sikre at observatøren fungerer som jeg forventer. MED TDD handler DET om å bygge gjenbrukbar kode i vanlig JavaScript. Det er fordeler med å skrive testbar kode i vanlig JavaScript. Test alt, og behold det som er bra for gjenbruk av kode.

med dette har vi fleshed ut EventObserver. Spørsmålet er, hva kan du bygge med dette?

The Observer Pattern In Action:En Blogg Word Count Demo

for demoen, tid til å sette på plass et blogginnlegg der det holder ordtellingen for deg. Hvert tastetrykk du skriver inn som input vil bli synkronisert av observer design mønster. Tenk på det som gratis skriving der hver hendelse branner en oppdatering til der du trenger det å gå.

for å få et ord teller fra gratis skriving, kan man gjøre:

const getWordCount = (text) => text ? text.trim().split(/\s+/).length : 0;

Ferdig! Det skjer mye i denne tilsynelatende enkle rene funksjonen, så hva med en ydmyk enhetstest? På denne måten er det klart hva jeg ment dette å gjøre:

// Arrangeconst blogPost = 'This is a blog \n\n post with a word count. ';// Actconst count = getWordCount(blogPost);// Assertassert.strictEqual(count, 9);

Merk den noe sprø inngangsstrengen inne blogPost. Jeg har tenkt at denne funksjonen skal dekke så mange kantsaker som mulig. Så lenge det gir meg en skikkelig ordtelling, går vi faktisk i riktig retning.

som en sidenote er DETTE den virkelige kraften TIL TDD. Man kan iterere på denne implementeringen og dekke så mange brukstilfeller som mulig. Enhetstesten forteller deg hvordan jeg forventer at dette skal oppføre seg. Hvis oppførselen har en feil, er det av en eller annen grunn lett å iterere og justere den. Med testen er det nok bevis igjen for enhver annen person å gjøre endringer.

tid for å koble disse gjenbrukbare komponentene til DOM. Dette er den delen hvor du kommer til å bruke vanlig JavaScript og sveise den rett inn i nettleseren.

en måte å gjøre det på ville være å ha FØLGENDE HTML på siden:

<textarea placeholder="Enter your blog post..." class="blogPost"></textarea>

Fulgt opp av Dette JavaScript:

const wordCountElement = document.createElement('p');wordCountElement.className = 'wordCount';wordCountElement.innerHTML = 'Word Count: <strong>0</strong>';document.body.appendChild(wordCountElement);const blogObserver = new EventObserver();blogObserver.subscribe((text) => { const blogCount = document.getElementById('blogWordCount'); blogCount.textContent = getWordCount(text);});const blogPost = document.getElementById('blogPost');blogPost.addEventListener('keyup', () => blogObserver.broadcast(blogPost.value));

Ta all din gjenbrukbare kode og sett på plass observatørdesignmønsteret. Dette vil spore endringer i tekstområdet og gi deg et ordtelling rett under det. Jeg bruker body.appendChild() I DOM API for å legge til dette nye elementet. Deretter feste hendelsen lyttere å bringe den til liv.

Merk med pilfunksjoner er det mulig å koble opp one-liner hendelser. Faktisk sender du hendelsesdrevne endringer til alle abonnenter med dette. () => blogObserver.broadcast() gjør mesteparten av arbeidet her. Den passerer selv i de siste endringene i tekstområdet rett inn i tilbakeringingsfunksjonen. Ja, klient-side scripting er super kul.

ingen demo er komplett uten en du kan berøre Og tweak, nedenfor Er CodePen:

Se Pennen Observer Mønster Av SitePoint (@SitePoint) På CodePen.

Nå vil jeg ikke kalle denne funksjonen komplett. Det er bare et utgangspunkt for observatørens designmønster. Spørsmålet i mitt sinn er, hvor langt er du villig til å gå?

Ser Fremover

det er opp til deg å ta denne ideen enda lenger. Det er mange måter du kan bruke observatørdesignmønsteret til å bygge nye funksjoner.

du kan forbedre demoen med:

  • En annen komponent som teller antall avsnitt
  • En annen komponent som viser en forhåndsvisning av angitt tekst
  • Forbedre forhåndsvisningen med markdown-støtte, for eksempel

Dette er bare noen få ideer du kan gjøre for å synke dypere inn i dette. Forbedringene ovenfor vil utfordre programmeringskoteletter.

Konklusjon

observatørdesignmønsteret kan hjelpe deg med å løse virkelige problemer I JavaScript. Dette løser det flerårige problemet med å holde en haug med elementer synkronisert med de samme dataene. Som ofte er tilfelle, når nettleseren brenner bestemte hendelser. Jeg er sikker på at de fleste av dere nå har kommet over et slikt problem og har kjørt mot verktøy og tredjepartsavhengigheter.

dette designmønsteret gir deg mulighet til å gå så langt fantasien din er villig til å gå. I programmering abstraherer du løsningen i et mønster og bygger gjenbrukbar kode. Det er ingen grense for hvor langt dette vil ta deg.

jeg håper du ser hvor mye, med litt disiplin og innsats, du kan gjøre i vanlig JavaScript. De nye funksjonene PÅ språket, FOR EKSEMPEL ES6, hjelper deg med å skrive litt kortfattet kode som kan gjenbrukes.

denne artikkelen ble fagfellevurdert Av Giulio Mainardi. Takk til Alle SitePoint peer reviewers for Å gjøre SitePoint innhold det beste det kan være!

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.