Programmazione ad oggetti in JSON

Gli oggetti JSON offrono un ottimo strumento per organizzare e mappare dati. Li usiamo per inviare informazioni al server con le chiamate AJAX, per rappresentare strutture dati, o anche semplicemente per rendere il codice più chiaro e leggibile. Grazie alla loro flessibilità e facilità di manipolazione possiamo inoltre introdurli all’interno del paradigma di programmazione orientata agli oggetti.

Supponiamo di voler gestire una lista di semplici aziende e per ognuna specificare le informazioni sulla sede, dipendenti, manager e quant’altro. Abbiamo quindi bisogno di definire degli oggetti, che prenderanno il nome più appropriato di classi, che ci daranno gli strumenti per stabilire cosa è un dipendente, un manager, ecc… e quali operazioni si possono fare su di loro.


Cominciamo con il modellare quindi le strutture dati per rappresentare i vari elementi tramite oggetti JSON con valori vuoti di default.

Identificheremo un dipendente come una persona con un ID identificativo, un nome, un ruolo lavorativo, chi è il suo capo e il nome dell’azienda per cui lavora.

Scriveremo le variabili in maiuscolo per far capire che servono a definire classi e proprietà degli elementi e non tanto per manipolare le informazioni.

var Employee = {
  id: 0,
  name: "",
  company: "",
  role: "",
  manager: ""
}

Il modello dati del manager sarà simile a quello del dipendente ma invece che definire un ruolo e chi è il suo manager di riferimento avrà le informazioni su quali siano i suoi dipendenti e quale progetto coordina.

var Manager = {
  id: 0,
  name: "",
  company: "",
  employees: [],
  project: ""
}

Ora potremmo creare il nostro primo dipendente e il nostro primo manager dell’azienda.

var firstEm = Employee;
firstEm.name = "John";
firstEm.role = "developer";
firstEm.company = "Blu Note";
...

var firstMa = Manager;
firstMa.name = "Riccardo";
firstMa.project = "Awesome Site";
....

Purtroppo in questa maniera quando andremo a definire nuove variabili con diversi valori, andremo anche a sovrascrivere quelle precedentemente create. Per evitare questo problema dovremo modificare le classi Person e Manager aggiungendo un metodo da invocare alla creazione di un nuovo elemento e che ci restituisca ogni volta un nuovo oggetto con cui lavorare.

var Employee = {
  create: function (params) {
    var params = params || {};
    return {
      id: params.id,
      name: params.name,
      company: params.company,
      role: params.role,
      manager: params.manager
    }
  }
}
var Manager = {
  create: function (params) {
    var params = params || {};
    return {
      id: 0,
      name: "",
      company: "",
      employees: [],
      project: ""
    }
  }
}

Ora siamo sicuri che invocando il metodo create delle rispettive classi otterremo un nuovo oggetto valorizzato con i parametri passati in input oppure un oggetto JSON vuoto.

var e1 = Employee.create({
  name: "John",
  role: "developer",
  company: "Blu Note"
});

var m1 = Manager.create({
  name: "Riccardo",
  employees: [e1],
  project: "Awesome site"
});

Potremo migliorare ulteriormente l’esempio incapsulando le informazioni condivise tra Employee e Manager in un oggetto più generico che estenderemo per evitare di dover riscrivere inutilmente le proprietà.

Possiamo definire una classe Person che useremo come classe di supporto per inizializzare le informazioni di base sia dei dipendenti che manager con un suo metodo create.

var Person = {
  create: function (params) {
    params = params || {};
    return {
      id: params.id,
      name: params.name,
      company: params.company
    }
  }
}

La classe Employee  estenderà la classe Person ereditandone tutte le caratteristiche e in più setterà valori aggiuntivi necessari a specificare le caratteristiche di un dipendente.

var Employee = {
 create: function (params) {
    var person = Person.create(params);
    person.role = params.role;
    person.manager = params.manager;
    return person;
  }
}

La classe Manager, come la classe Employee, estenderà Person e setterà i suoi valori aggiuntivi.

var Manager = {
  create: function (params) {
    var person = Person.create(params);
    person.project = params.project;
    person.eployees = params.employees;
    return person;
  }
}

In questa maniera potremo creare dipendenti e manager semplicemente invocando il metodo create relativamente di Employee e Manager.

var e1 = Employee.create({
  name: "John",
  role: "developer",
  company: "Blu Note"
});

var e2 = Employee.create({
  name: "Smith",
  role: "designer",
  manager: "Riccardo"
});

var e3 = Employee.create();

var m1 = Manager.create({
  name: "Riccardo",
  employees: [e1, e2],
  project: "Awesome site"
});

Importante da tenere in considerazione che con questa struttura al momento della creazione di un elemento non è necessario passare tutti i valori perchè la classe Person comunque ha il compito di ritornare un oggetto JSON che potrà essere manipolato successivamente.


Supponiamo ora di voler avere a disposizione un metodo copy per facilitare le operazioni di creazione di elementi.

Basterà modificare opportunamente la classe Person per ereditarne “gratis” la funzionalità sia in Employee che in Manager.

var Person = {
  create: function (params) {
    params = params || {};
    return {
      id: params.id,
      name: params.name,
      company: params.company,
      copy: function(opt){
        var opt = opt || {};
        var person = this;
        var clone = {};
        for (var prop in person) {
          if (opt[prop]) {
            clone[prop] = opt[prop]
          } else if (person.hasOwnProperty(prop)) {
            clone[prop] = person[prop]
          }
        }
        return clone
      }
    }
  }
}

In questa maniera potremo invocare il metodo copy direttamente dalla variabile creata per ottenere un nuovo oggetto con gli stessi valori, a meno di quelli che gli passiamo in input.

var e4 = e1.copy();
var e5 = e1.copy({name: "Karl"})

e1.name // "John"
e4.name // "John"
e5.name // "Karl"

La porola chiave this ci permette infatti di contestualizzare la lettura dei valori all’oggetto dal quale invochiamo il metodo copy.

In alternativa avremmo potuto definire il metodo copy a livello di classe e quindi ad esempio dentro Manager, in modo che vada a copiare le chiavi di un primo oggetto passato in input e al più sovrascriverle con un secondo parametro.

var m2 = Manager.copy(m1);
var m3 = Manager.copy(m1, {name: "Smith"})

m1.name // "Riccardo"
m2.name // "Riccardo"
m3.name // "Smith"