Ereditarietà ed estensione di oggetti con JavaScript

Se si ha familiarità con la programmazione orientata agli oggetti, è molto probabile che si abbia familiarità con la sottoclasse e l'ereditarietà. Tuttavia, l'ereditarietà ha subito un brutto colpo. Credo che ciò sia dovuto al fatto che alcuni sviluppatori lo vedono come una soluzione universale quando è necessario modificare un programma. Il problema con questo è che le gerarchie di classi possono diventare ingestibili. 

Esistono altri modelli di progettazione che possiamo utilizzare per rendere le nostre app più facili da comprendere e pronte per il cambiamento. Ti mostrerò alcuni esempi di come puoi usare l'ereditarietà e il decoratore e il pattern composito per migliorare la progettazione del tuo programma.

Eredità

L'idea dietro l'ereditarietà è che un oggetto "è una" versione specializzata di un altro oggetto. Esiste una classe genitore (nota anche come superclasse) che definisce le proprietà di base del nostro oggetto. E c'è una classe figlia (sottoclasse) che eredita le proprietà della classe genitore. 

Un esempio di ereditarietà è un cane e un barboncino. Tutti i cani hanno alcune caratteristiche come quattro zampe e la capacità di abbaiare. Un barboncino "è un" tipo di cane. Un SUV è un veicolo. Un cerchio è una forma. Ecco come sarebbe la nostra gerarchia di classi se stessimo progettando un programma per la creazione di forme.

Il vantaggio di avere una classe Shape è che possiamo riutilizzare le proprietà e i metodi che abbiamo definito in altre classi. 

Si noti che il getArea il metodo è stato ridefinito in ciascuna delle nostre sottoclassi. Non abbiamo dovuto ridefinire questo metodo, ma lo abbiamo fatto per sostituire l'implementazione del metodo da parte dei nostri genitori. Questo perché ogni forma avrà il proprio modo di calcolare l'area. 

L'override di un metodo genitore in una classe figlia è un esempio di polimorfismo. Il polimorfismo è la capacità degli oggetti di avere più forme. Consente alle sottoclassi di avere metodi con lo stesso nome della loro superclasse ma con diverse implementazioni.  

Questo è un esempio di come sarebbe il nostro codice per la creazione di una sottoclasse Shape e Circle:

class Shape costruttore (x, y) this.xPosition = x; this.yPosition = y;  getArea () ... classe Circle estende Shape costruttore (x, y, raggio) super (x, y, raggio); this.radius = radius getArea () ... lascia circle = new Circle (1,2,3);

Uno degli svantaggi di questa scelta progettuale è che se decidi che la classe genitore deve cambiare, allora anche le sottoclassi potrebbero dover cambiare, insieme a tutti gli oggetti che abbiamo creato. 

Ad esempio, supponiamo di aver deciso in seguito che sarebbe stato meglio sostituire i parametri x e y della classe Shape con un oggetto. Di conseguenza, avremmo bisogno di cambiare il costruttore per tutte le nostre sottoclassi e gli argomenti per ogni oggetto che abbiamo istanziato.

Possiamo vedere come questo può facilmente diventare problematico. Dovremmo assicurarci di avere il nostro progetto giusto la prima volta, in modo da evitare di apportare modifiche. Ma questo non è pratico, né è ciò per cui dovremmo mirare. Un programma è un'entità in continua evoluzione, ed è meglio per noi sviluppatori se abbiamo la flessibilità di apportare modifiche facilmente. Per lo meno, non dovremmo avere più di un livello di classi di bambini.

Decoratore

Un decoratore ci consente di associare le proprietà a un oggetto dopo che sono state create. Ciò significa che possiamo aggiungere funzionalità senza sottoclasse o preoccuparci dell'implementazione del nostro oggetto. 

Invece di pensare che un cerchio sia una forma, potremmo usare la classe Shape per creare cerchi e avvolgerlo con le proprietà aggiuntive che vogliamo. Ecco un'alternativa alla creazione di oggetti cerchio usando un decoratore:

class Shape costruttore (dati) this.x = data.x; this.y = data.y;  getArea () ... function CircleDecorator (shape) shape.radius = 3; shape.getArea = function () ...; forma di ritorno;  let shape = new Shape (x: 1, y: 2); lascia circle = new CircleDecorator (shape);

Possiamo aggiungere o modificare i membri della nostra classe Shape con un decoratore invece di sottoclassi. Con l'esempio delle forme, potresti scoprire di voler creare un oggetto cerchio che abbia tutte le proprietà di cui hai bisogno e faccia lo stesso per altre forme. Questo va bene. Ma un decoratore ci consente di riutilizzare il codice nella nostra classe Shape e modificarlo con la funzionalità che differisce da ogni forma. Di conseguenza, avremo più oggetti, ma più oggetti sono più facili da gestire rispetto a più sottoclassi.

Questo modello non si limita alla creazione di grafica. È possibile utilizzarlo in qualsiasi situazione in cui si desidera aggiungere responsabilità a un oggetto. 

Ad esempio, potremmo avere una classe che gestisce l'iscrizione degli utenti al nostro account. Prima di salvare le loro informazioni nel nostro database, sarebbe opportuno verificare che l'input sia valido e non contenga script dannosi. Potremmo decorare la classe con un metodo per convalidare prima le informazioni. Questo stesso decoratore può essere riutilizzato ovunque nella nostra applicazione in cui accettiamo l'input dell'utente.

Pattern composito

Gli oggetti possono essere composti da altri oggetti. La vista per un'app può essere pensata come un componente composto da altre viste. Se stessimo creando un gioco, il nostro mondo di gioco mostrerebbe tutti i nostri elementi grafici che abbiamo creato come cerchi e quadrati. Ogni volta che aggiorniamo la nostra vista, dovremmo ridisegnare ogni elemento. Abbiamo bisogno di un modo per gestire tutti gli elementi come gruppo. 

È qui che il modello composito può aiutarci. Possiamo creare una classe che è responsabile di tutti gli elementi. Quando vogliamo ridisegnare gli elementi, chiamiamo il metodo draw di questa classe e chiameremo il metodo draw su ogni singolo elemento.  

class Component costruttore (nome) this.components = []; this.element = document.createElement (nome);  add (elem) this.components.push (elem);  draw () for (const elem di this.components) elem.draw ();  class Circle costruttore (dati) this.x = data.x; this.y = data.y; this.radius = data.radius;  getArea () ... draw () ... let world = new Component ('div'); lascia circle = new Circle (x: 1, y: 1, raggio: 2); lascia circle2 = new Circle (x: 10, y: 10, raggio: 2); world.add (cerchio); world.add (circle2); world.draw ();

Una pagina web può anche essere pensata come un componente. Questo componente potrebbe avere un menu, una barra laterale e un post sul blog. Il post sarebbe un sottocomponente con un'immagine, un titolo e un corpo. Il disegnare il metodo per la nostra componente principale dell'app richiamerà il disegno nel menu, nella barra laterale e nel post. Il post componente a sua volta chiamerà draw sull'immagine, titolo e corpo del post. 

Ecco una vista di ciò che una pagina Web vorrebbe divisa in componenti:

Questo modello non è limitato alla creazione di viste. Ad esempio, se stessimo creando un gioco, potremmo avere un componente per gestire gli elementi sullo schermo, un componente per gestire l'audio e un componente per gestire lo stato del gioco. 

Questi sarebbero tutti all'interno di un componente che chiamo il motore di gioco. Il motore di gioco avrebbe un metodo di inizializzazione che chiamerebbe il metodo di inizializzazione su ciascuno dei suoi sotto-componenti. Questa è la potenza nell'uso del modello composito. Invece di trattare con singoli oggetti, possiamo trattarli come un unico oggetto.  

Conclusione

L'ereditarietà ci consente di riutilizzare il codice definendo un oggetto in termini di un altro oggetto. Il pattern decoratore ci consente di aggiungere responsabilità a un oggetto senza modificare il codice originale o la sottoclasse. Lo schema composito modella le relazioni parziali. Questi modelli non sono pensati per essere usati isolatamente. 

JavaScript è diventato il linguaggio di lavoro sul web. Non è senza le sue curve di apprendimento, e ci sono un sacco di quadri e librerie per tenerti occupato, pure. Se stai cercando ulteriori risorse da studiare o da utilizzare nel tuo lavoro, dai un'occhiata a ciò che abbiamo a disposizione su Envato Market.

Possono essere combinati come meglio credi. Gli esempi che ho fornito non dovrebbero essere considerati come l'unica applicazione per l'utilizzo dei pattern. Sono solo una guida. Sentiti libero di usare la tua creatività per applicarli. Non esiste un modo per implementarli o un caso d'uso.