Incapsulamento dei dati in JavaScript: getter e setter

Incapsulamento dei dati in JavaScript: getter e setter

Quando si costruiscono grandi applicazioni JavaScript, c’è presto la necessità di dividerle in moduli collegati da contratti chiari. Quando si tratta di manutenzione a lungo termine, è necessario fornire un modo per mantenere la compatibilità con i cambiamenti di interfaccia. Per fare questo, i linguaggi orientati agli oggetti si affidano all’incapsulamento o alle informazioni che nascondono i dettagli di implementazione agli utenti di un pezzo di codice, in modo che possa cambiare senza impattare sui client.

In questo post parleremo dell’incapsulamento dei dati in JavaScript. Più specificamente, parleremo dell’incapsulamento delle proprietà dietro getter e setter.

Alcuni concetti

JavaScript è stato progettato avendo come base il principio della tipizzazione delle anatre:

“Quando vedo un uccello che cammina come un’anatra e nuota come un’anatra e starnazza come un’anatra, chiamo quell’uccello un’anatra.”

In altre parole, nel suo nucleo, JavaScript è un linguaggio orientato agli oggetti, ma diversamente da altri linguaggi orientati agli oggetti come C# e Java, in JavaScript le classi sono cittadini di seconda classe e le interfacce non esistono. Gli oggetti non hanno tipi, e quindi il codice deve essere scritto per interfacciarsi agli oggetti che hanno certe proprietà e metodi. I contratti tra moduli sono basati su un insieme di metodi e proprietà che si presume siano presenti negli oggetti scambiati.

L’incapsulamento è fondamentale nella programmazione orientata agli oggetti. Significa che un dato oggetto dovrebbe essere in grado di nascondere i dettagli del suo funzionamento interno, in modo che gli altri oggetti che interagiscono non dipendano dai dettagli della sua implementazione, ma da un’interfaccia di alto livello concordata. Questo rende la vita degli sviluppatori più semplice, sia dal lato del fornitore che dal lato degli utenti, poiché entrambi sanno che i dettagli dell’implementazione possono cambiare senza rompere gli oggetti client.

Perché esporre dati su un’interfaccia è una cattiva idea

In JavaScript un oggetto è semplicemente un dizionario di nomi di proprietà mappati su valori, quando il valore di una proprietà è una funzione, lo chiamiamo metodo. L’interfaccia tra i metodi è l’insieme delle proprietà che il codice client si aspetta di trovare su un oggetto.

Se un oggetto espone solo metodi, è facile far evolvere l’interfaccia col tempo. Un metodo può controllare i suoi parametri e reagire di conseguenza. È anche possibile creare nuovi metodi che forniscono nuove caratteristiche. E, supportando quelli vecchi che adattano il vecchio comportamento al nuovo. Con le proprietà non è possibile farlo.

Nel caso delle proprietà, mantenere un’interfaccia stabile non è facile. Perché, per default, il codice client può fare riferimento a proprietà non esistenti di un oggetto. La scrittura di una proprietà non esistente la creerà. La sua lettura restituirà undefined. Il codice client non sarà in grado di rilevare rapidamente se sta usando proprietà deprecate. Peggio ancora, l’errore può propagarsi ad altre parti del codice, rendendo più difficile l’individuazione del problema.

L’API JavaScript standard ha metodi che possono essere utili per evitare tali problemi: è possibile congelare o sigillare un oggetto, in modo che i nomi delle proprietà non supportate non possano essere utilizzati.

Getters/Setters per salvare la giornata

Nascondere i dettagli dell’implementazione del metodo è facile.

Getters/setters sono stati ufficialmente introdotti nel linguaggio in ECMAScript 5.1 (ECMA-262). Sono attualmente supportati in tutti i principali browser desktop e mobili.

L’idea di base è che aggiunge la sintassi per definire proprietà accessorie come metodi, invece di semplici proprietà di dati. Un getter è definito dalla parola chiave get seguita da una funzione con il nome della proprietà, che non prende argomenti e restituisce il valore della proprietà. Un setter è definito dalla parola chiave set seguita da una funzione denominata dopo la proprietà che prende il nuovo valore della proprietà come parametro.

L’esempio seguente illustra un getter e un setter usati per definire una proprietà accessor chiamata prop:

var obj = { v: 0, get prop() { return this.v; }, set prop(newValue) { this.v = newValue; }};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);

Output:

042

Si può anche creare una proprietà di sola lettura definendo un getter senza setter:

var obj = { get prop() { return -1; },};console.log(obj.prop);obj.prop = 42;console.log(obj.prop);

Output:

-1-1

Una proprietà può quindi essere sostituita da un paio di metodi che nel caso in cui l’implementazione di una classe cambi, possono reagire ad essa e adattare il comportamento della classe. Nel caso peggiore, può sollevare un’eccezione per dire all’utente che la proprietà è deprecata e non dovrebbe più essere usata.

Altra lettura

Spero che tu abbia imparato qualcosa di nuovo sulla progettazione di applicazioni evolutive complesse in JavaScript in questo post. In questa sezione, vi darò un paio di link a link che contengono maggiori informazioni.

Articoli introduttivi

Questa sezione contiene un paio di brevi articoli che presentano i concetti e forniscono una panoramica delle relative API:

  1. getter e setter di proprietà.
  2. JavaScript Getters and Setters

Articoli più avanzati

Questi articoli parlano di argomenti più avanzati come i problemi causati da getter e setter, e come risolverli.

  1. Perché getters/setters è una cattiva idea in JavaScript
  2. Incapsulamento dei dati in JavaScript

Documentazione di riferimento

Infine, per padroneggiare veramente l’argomento, i link alla documentazione di mozilla delle relative API:

  1. get e set permettono di definire getters e setters. Oltre a quello che abbiamo spiegato qui. Ci sono altre caratteristiche interessanti come il supporto per nomi di proprietà generati dinamicamente, e l’uso di getter e setter per simulare l’impostazione/il recupero di valori in un array
  2. seal e freeze permettono di controllare quali proprietà di un oggetto sono modificabili o meno e come. Può essere usato per evitare che i client usino parti deprecate dell’API di un oggetto.
  3. defineProperty permette di definire getter e setter e allo stesso tempo di avere più controllo su come queste proprietà sono viste dai client e come sono modificabili.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.