JavaScript Design Patterns: The Observer Pattern

In JavaScript, c’è un problema che si presenta spesso. È necessario un modo per aggiornare parti di una pagina in risposta a determinati eventi, con i dati forniti. Supponiamo, ad esempio, l’input dell’utente che viene quindi proiettato in uno o più componenti. Questo porta in un sacco di push-and-pull nel codice per mantenere tutto in sincronia.

Questo è dove il modello di progettazione observer può aiutare — consente l’associazione di dati uno-a-molti tra gli elementi. Questa associazione di dati unidirezionale può essere guidata dagli eventi. Con questo modello, è possibile creare codice riutilizzabile che risolve per le vostre esigenze specifiche.

In questo articolo, mi piacerebbe esplorare il modello di progettazione observer. Ti aiuterà a risolvere un problema comune che vedi nello scripting lato client. Questo è l’associazione di dati uno-a-molti, unidirezionale e basata su eventi. È un problema che si presenta spesso quando si hanno molti elementi che devono essere sincronizzati.

Userò ECMAScript 6 per illustrare il modello. Sì, ci saranno classi, funzioni freccia e costanti. Sentitevi liberi di esplorare questi argomenti da soli se non si è già familiarità. Userò parti di ES6 che introducono solo zucchero sintattico, quindi è portatile con ES5 se necessario.

E userò Test-Driven-Development (TDD) per lavorare sul modello. In questo modo hai un modo per sapere come ogni componente è utile.

Le nuove funzionalità del linguaggio in ES6 creano un codice succinto. Quindi, iniziamo.

L’Osservatore di eventi

Una vista di alto livello del modello è simile a questa:

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

Dopo aver rimpolpato il modello di osservatore, aggiungerò un conteggio delle parole che lo utilizza. Il componente di conteggio delle parole prenderà questo osservatore e riunirà tutto.

Per inizializzare il EventObserver do:

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

Inizia con un elenco vuoto di eventi osservati e fallo per ogni nuova istanza. D’ora in poi, aggiungiamo più metodi all’interno di EventObserver per rimpolpare il modello di progettazione.

Il metodo di sottoscrizione

Per aggiungere nuovi eventi:

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

Afferra l’elenco degli eventi osservati e spingere un nuovo elemento alla matrice. L’elenco degli eventi è un elenco di funzioni di callback.

Un modo per testare questo metodo in JavaScript semplice è il seguente:

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

Io uso le asserzioni dei nodi per testare questo componente nel nodo. Le stesse affermazioni esatte esistono anche come affermazioni Chai.

Nota l’elenco degli eventi osservati consiste in umili callback. Quindi controlliamo la lunghezza dell’elenco e affermiamo che il callback è nell’elenco.

Il metodo di cancellazione

Per rimuovere gli eventi:

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

Filtra dall’elenco tutto ciò che corrisponde alla funzione di callback. Se non c’è corrispondenza, il callback arriva a rimanere sulla lista. Il filtro restituisce un nuovo elenco e riassegna l’elenco degli osservatori.

Per testare questo bel metodo, fare:

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

Il callback deve corrispondere alla stessa funzione presente nell’elenco. Se esiste una corrispondenza, il metodo unsubscribe la rimuove dall’elenco. Nota il test utilizza il riferimento alla funzione per aggiungerlo e rimuoverlo.

Il metodo di trasmissione

Per chiamare tutti gli eventi fare:

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

Questo scorre l’elenco degli eventi osservati ed esegue tutti i callback. Con questo, ottieni la necessaria relazione uno-a-molti con gli eventi sottoscritti. Si passa il parametro data che rende associati i dati di callback.

ES6 rende il codice più efficace con una funzione freccia. Nota la funzione (subscriber) => subscriber(data) che esegue la maggior parte del lavoro. Questa funzione freccia one-liner beneficia di questa breve sintassi ES6. Questo è un netto miglioramento del linguaggio di programmazione JavaScript.

Per testare questo metodo di trasmissione, fare:

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

Usa let invece di un const in modo da poter cambiare il valore della variabile. Questo rende la variabile mutabile che mi permette di riassegnare il suo valore all’interno del callback. L’utilizzo di un let nel codice invia un segnale agli altri programmatori che la variabile sta cambiando ad un certo punto. Ciò aggiunge leggibilità e chiarezza al codice JavaScript.

Questo test mi dà la sicurezza necessaria per garantire che l’osservatore funzioni come mi aspetto. Con TDD, si tratta di creare codice riutilizzabile in semplice JavaScript. Ci sono vantaggi nella scrittura di codice testabile in JavaScript semplice. Prova tutto e mantieni ciò che è buono per il riutilizzo del codice.

Con questo, abbiamo concretizzato il EventObserver. La domanda è: cosa puoi costruire con questo?

The Observer Pattern in Action: A Blog Word Count Demo

Per la demo, è ora di mettere in atto un post sul blog in cui mantiene il conteggio delle parole per te. Ogni battitura immessa come input verrà sincronizzata dal modello di progettazione dell’osservatore. Pensalo come input di testo libero in cui ogni evento genera un aggiornamento su dove è necessario andare.

Per ottenere un conteggio delle parole dall’input di testo libero, si può fare:

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

Fatto! C’è molto da fare in questa funzione pura apparentemente semplice, quindi che ne dici di un umile test unitario? In questo modo è chiaro cosa intendevo fare:

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

Nota la stringa di input un po ‘ stravagante all’interno di blogPost. Intendo che questa funzione copra il maggior numero possibile di casi limite. Fintanto che mi dà un conteggio parola corretta stiamo andando, infatti, nella giusta direzione.

Come nota a margine, questo è il vero potere di TDD. Si può iterare su questa implementazione e coprire il maggior numero possibile di casi d’uso. Il test unitario ti dice come mi aspetto che questo si comporti. Se il comportamento ha un difetto, per qualsiasi motivo, è facile iterare e modificarlo. Con il test, ci sono prove sufficienti per qualsiasi altra persona per apportare modifiche.

È ora di collegare questi componenti riutilizzabili al DOM. Questa è la parte in cui si arriva a maneggiare semplice JavaScript e saldare a destra nel browser.

Un modo per farlo sarebbe avere il seguente HTML sulla pagina:

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

Seguito da questo 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));

Prendi tutto il tuo codice riutilizzabile e metti in atto il modello di progettazione dell’osservatore. Questo terrà traccia delle modifiche nell’area di testo e ti darà un conteggio delle parole proprio sotto di esso. Sto usando body.appendChild() nell’API DOM per aggiungere questo nuovo elemento. Quindi, allegando gli ascoltatori dell’evento per portarlo alla vita.

Nota con le funzioni freccia è possibile collegare eventi one-liner. In effetti, trasmetti le modifiche guidate dagli eventi a tutti gli abbonati con questo. Il () => blogObserver.broadcast() fa la maggior parte del lavoro qui. Passa anche le ultime modifiche all’area di testo direttamente nella funzione di callback. Sì, lo scripting lato client è super cool.

Nessuna demo è completa senza uno che puoi toccare e modificare, di seguito è riportato il CodePen:

Vedi la penna The Observer Pattern di SitePoint (@SitePoint) su CodePen.

Ora, non chiamerei questa funzione completa. È solo un punto di partenza del modello di progettazione dell’osservatore. La domanda nella mia mente è, fino a che punto sei disposto ad andare?

Guardando avanti

Sta a voi prendere questa idea ancora di più. Esistono molti modi in cui è possibile utilizzare il modello di progettazione observer per creare nuove funzionalità.

È possibile migliorare la demo con:

  • Un altro componente che conta il numero di paragrafi
  • Un altro componente che mostra un’anteprima del testo inserito
  • Migliora l’anteprima con il supporto markdown, ad esempio

Queste sono solo alcune idee che puoi fare per approfondire questo aspetto. I miglioramenti di cui sopra metteranno alla prova le tue braciole di programmazione.

Conclusione

Il modello di progettazione observer può aiutarti a risolvere problemi reali in JavaScript. Questo risolve il problema perenne di mantenere un gruppo di elementi sincronizzati con gli stessi dati. Come spesso accade, quando il browser attiva eventi specifici. Sono sicuro che la maggior parte di voi ormai si è imbattuta in un problema del genere e si è rivolta a strumenti e dipendenze di terze parti.

Questo modello di progettazione si equipaggia ad andare per quanto la vostra immaginazione è disposta ad andare. Nella programmazione, si astrae la soluzione in un modello e si crea codice riutilizzabile. Non c’è limite a quanto questo vi porterà.

Spero che tu veda quanto, con un po ‘ di disciplina e sforzo, puoi fare in semplice JavaScript. Le nuove funzionalità della lingua, come ES6, ti aiutano a scrivere un codice succinto riutilizzabile.

Questo articolo è stato peer reviewed da Giulio Mainardi. Grazie a tutti i peer reviewer di SitePoint per rendere i contenuti di SitePoint il meglio che può essere!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.