3. Programmazione ad oggetti in Swift Flashcards
Programmazione ad oggetti in Swift
Uno dei paradigmi di sviluppo più affascinanti degli ultimi anni, ultimi anni si fa per dire, è la programmazione ad oggetti.
Un paradigma è un insieme di regole che ti guidano verso la programmazione e sviluppo di un software o applicazione. Grazie alla programmazione ad oggetti ti troverai a vedere il codice e le applicazioni tutte da un’altra prospettiva. Se capita fino in fondo, la programmazione ad oggetti, ha effetti sul modo di pensare e interpretare il mondo che ti circonda.
La programmazione ad oggetti è filosofia, pura astrazione.
Grazie alla programmazione ad oggetti del linguaggio Swift arriverai agli stessi concetti che ti porteranno a sviluppare la tua prima applicazione. Già da questa lezione, puoi cominciare a dare uno sguardo al mio prossimo corso o ai tutorial sul blog.
Anche se la programmazione ad oggetti in Swift è decisamente semplificata rispetto agli altri linguaggi, partiamo dal principio e rispondiamo a queste semplici domande.
Perché è stata chiamata ad oggetti? e cos’è un oggetto?
Cos’è un oggetto in un linguaggio di programmazione
Ogni cosa è un oggetto. il MacBook da cui ti sto scrivendo, la cotoletta che hai mangiato a pranzo, la penna stilo con cui scrivi e persino il tuo migliore amico è un oggetto.
Ma, cosa rende qualcosa un oggetto?
Un oggetto è un’entità che può essere identificata da delle proprietà specifiche. Per proprietà intendo sia i suoi attributi che le sue funzionalità.
Ognuno di questi bellissimi Husky ha degli attributi unici che l’altro non ha. Il colore del pelo, il peso, il nome, il colore degli occhi e così via. Ognuno ha anche delle funzionalità diverse rispetto al vicino. Uno corre in un modo, l’altro ulula in modo diverso…
Cioè un oggetto è qualcosa che puoi categorizzare mediante delle proprietà (inteso come caratteristiche). Una penna sai che è una penna perché ha quella particolare forma e funzionalità. Un cane sai che è diverso da un gatto perché abbaia e sicuramente non miagola.
Ti faccio un altro esempio, io (Giuseppe) sono un oggetto caratterizzato dagli attributi: data di nascita, colore della pelle, altezza, QI, colore capelli e chi più ne ha più ne metta. Inoltre ho delle funzionalità: posso camminare, saltare, studiare ecc ecc.
Lo scopo della programmazione ad oggetti con il linguaggio Swift è quello di permettere, ad uno sviluppatore, di poter raffigurare oggetti e comportamenti informatici con lo stesso sistema che utilizziamo noi essere umani quando descriviamo la realtà che ci circonda.
Due oggetti simili, come me e Matteo che siamo entrambe Persone e quindi caratterizzati dagli stessi attributi, siamo diversi perché possediamo dei valori non uguali che caratterizzano i nostri attributi. Io sono nato giorno x e lui giorno y, io sono alto 1 e lui 2.
Adesso puoi dedurre che i valore assunti dagli attributi definiscono lo stato dell’oggetto. Quindi due oggetti, apparentemente simili, sono diversi quando hanno uno stato diverso.
La classe, il modello astratto di un oggetto
Nell’ipotetico caso in cui tu non avessi conosciuto la razza Husky, cos’è che avresti detto guardando quella foto? Sicuramente avresti detto: “Ma che bellini quei cagnolini”.
Perché sei andato sul sicuro dicendo che quelli sono cani?
Perché sai che i cani, pur essendo diversi in razze, hanno tutti la stessa fisionomia e caratteristiche.
Cioè sai per certo che hanno un pelo, delle zampe, che abbaiano ecc. E, di conseguenza, in base al valore reale di quelle caratteristiche sai se un cane appartiene ad una razza o un’altra. Un Pastore Tedesco è diverso da un Husky ma entrambi sono comunque Cani.
Oppure, io e tu siamo per certo delle persone, e non dei cani (qualcuno lo è, ndr), perché abbiamo degli attributi e funzionalità che ci rendono diversi da loro.
Però, io e te, siamo comunque diversi perché abbiamo valori diversi. Io sono alto x, tu y. Io ho i capelli scuri, tu x e così via.
A questo punto, introduciamo un nuovo elemento.
Gli oggetti con le stesse proprietà fanno parte della stessa categoria. Queste categorie prendono il nome di Classi.
Una Classe è il modello astratto di un oggetto, uno schema che rappresenta in modo concettuale un oggetto.
La rappresentazione reale della classe, che chiamerai istanza della classe, prende il nome di oggetto.
Attenzione! Dire Oggetto e dire Classe non equivale a dire la stessa cosa! (Cosa che purtroppo si sente dire).
Matteo è l’istanza, quindi un oggetto, della classe Persona, Greg (il mio cane) è l’istanza della classe Cane, il MacBook è un oggetto della classe Portatile e così via.
É la classe a definire gli attributi e le funzionalità di un oggetto. Quest’ultimo, essendo un’istanza della classe, ne eredita tutte le proprietà.
// pseudo codice di una classe
classe Persona {
//attributi
nome
data_nascita
altezza
peso
codice_fiscale
//funzionalità
camminare()
bere()
studiare()
}
//creazione di un’istanza della classe
Giuseppe = Persona()
Creare una Classe con il linguaggio Swift
Ripetere le cose non fa mai male.
Una classe è la rappresentazione astratta di un oggetto, ne definisce le proprietà e il comportamento (funzionalità).
Con il linguaggio Swift, la rappresentazione di una classe è fatta precedere dalla parola chiave class seguito dal nome e dalle parentesi graffe:
class UnaClasse {
//proprietà e comportamento
}
Le regole del buon programmatore impongono che la prima lettera del nome della classe sia scritta in Maiuscolo.
Proviamo a creare una semplice classe per poter descrivere un Cane. Come proprietà definire solamente la razza, il colore ed il peso.
class Cane {
//proprietà
var razza: String?
var colore: String?
var peso: Float?
}
Le proprietà della classe, come puoi notare, sono delle semplicissime variabili (o costanti) che ci permetteranno di descrivere l’oggetto con dei valori.
Abbiamo semplicemente definito in maniera ideale quali sono le proprietà di un Cane qualsiasi. Non abbiamo messo dei valori di default perché, come dicevamo, ogni oggetto ha i suoi valori unici. Questo è il motivo per cui ho lasciato le proprietà opzionali.
Adesso che hai definito la classe Cane è arrivato il momento di creare degli oggetti Cane reali.
Istanziare una classe. Il processo di creazione di un oggetto
Quando nasce un bambino gli viene assegnato un nome. Si registra il peso di nascita, si vede di che colore ha i capelli e cose di questo genere.
Ti ho detto poc’anzi che la classe descrive in maniera generale e ipotetica una tipologia d’oggetti ma non da i valori reali di quell’oggetto perché ogni oggetto è unico.
class Persona {
var nome: String?
var peso: Double?
var colore_capelli: String?
}
Si sono appena conclusi i fatidici nove mesi!! Sei diventato papà/mamma!!
Come faccio a creare un oggetto della classe Persona?
Init e la costruzione dell’oggetto
Qui entra in gioco il costruttore. Il costruttore è una funzione particolare che serve, per l’appunto, a definire quali saranno i valori reali da dare all’oggetto al momento della sua creazione.
Il costruttore o init è una funzione del linguaggio Swift a tutti gli effetti. Come parametri si mettono gli stessi attributi della classe che si vorranno inizializzare quando si creerà l’oggetto.
class Persona {
var nome: String?
var peso: Double?
var colore_capelli: String?
init(nuovoNome: String, nuovoPeso: Double, nuovoColoreCapelli: String){
}
}
Dentro la funzione init dovrai passare i valori, che l’utente metterà nei parametri, alle proprietà della classe:
init(nuovoNome: String, nuovoPeso: Double, nuovoColoreCapelli: String) {
nome = nuovoNome
peso = nuovoPeso
colore_capelli = nuovoColoreCapelli
}
A questo punto, quando dovrai creare un nuovo oggetto Persona (perché è nato il famoso bimbo) potrai utilizzare la sintassi NomeClasse.init(<parametri>):</parametri>
var giuseppe = Persona.init(nuovoNome: “Giuseppe”, nuovoPeso: 3.55, nuovoColoreCapelli: “neri”)
var delia = Persona.init(nuovoNome: “Delia”, nuovoPeso: 4.2, nuovoColoreCapelli: “castano chiaro”)
var luca = Persona.init(nuovoNome: “Luca”, nuovoPeso: 3.99, nuovoColoreCapelli: “rossi”)
Finalmente hai creato dei nuovi oggetti figli della classe Persona. Come lo hai fatto? mediante l’utilizzo della funzione speciale init che serve, per l’appunto, a creare un nuovo oggetto della classe su cui viene utilizzato.
Accedere alle proprietà della classe
A questo punto hai tre nuovi oggetti di tipo Persona. Nel caso in cui volessi leggere i valori delle loro proprietà, potrai farlo utilizzando la notazione nomeOggetto.nomeProprietà. Per esempio, se volessimo leggere i nomi dei tre oggetti, scriverei:
giuseppe.nome
delia.nome
luca.nome
Init senza parametri
Una classe può essere costruita anche con un init senza parametri solo se le sue proprietà sono opzionali oppure hanno dei valori di default.
Le classe che hanno questa caratteristica possono usare un init() presente di default.
class Persona {
var nome: String?
var peso: Double?
var colore_capelli: String?
}
var nuovaPersona = Persona.init()
nuovaPersona.nome = “Giuseppe”
nuovaPersona.peso = 65
nuovaPersona.colore_capelli = “neri”
Grazie a questo init vuoto puoi istanziare le proprietà dell’oggetto in un secondo momento.
Nell’esempio ho creato l’oggetto nuovaPersona e successivamente ho dato dei valori alle sue proprietà. Questo caso può essere utile quando, in un’applicazione, vuoi dare i valori di un oggetto solamente dopo determinati passaggi.
Nel caso in cui fosse presente un init all’interno della classe, l’init senza parametri non potrà più essere utilizzato:
class Persona {
var nome: String?
var peso: Double?
var colore_capelli: String?
init(nuovoNome: String, nuovoPeso: Double, nuovoColoreCapelli: String) {
nome = nuovoNome
peso = nuovoPeso
colore_capelli = nuovoColoreCapelli
}
}
// Errore
var nuovaPersona = Persona.init() // Non puoi utilizzare l’init senza parametri. Utilizza l’init che hai definito nella classe
Proprietà opzionali o no?
Quando abbiamo definito la classe Persona abbiamo anche messo tre proprietà: nome, peso e colore_capelli. Queste tre proprietà le abbiamo anche messe opzionali perché, come ti dicevo, non possiamo definire a priori un valore di default per queste proprietà.
Ovvero, dato che non tutte le persone hanno lo stesso nome, lo stesso peso ecc, l’opzionalità ci da modo di evitare l’assegnazione di un valore.
C’è una regola da rispettare.
Le proprietà della classe, se non sono opzionali, devono essere tutte inizializzate alla costruzione dell’oggetto (cioè quando si usa la funzione init).
Facciamo un esempio.
La nostra applicazione gestisce una fattoria con delle Mucche. Ogni mucca deve avere un codice numerico che la identifica. Però non vogliamo lasciarlo opzionale questo valore perché non deve essere più modificato. Quindi ci serve una costante.
class Mucca {
let codice: String
}
Vedrai che il Playground ti avviserà di un errore.
L’errore di comunica che non puoi lasciare le proprietà della classe senza valori di default, opzionalità o senza che un init le riempia.
Lui ti consiglia di mettere un valore di default, cioè una stringa vuota “”. Io ti consiglio, dato anche che l’esercizio chiedeva questo, di mettere un init con un parametro:
class Mucca {
let codice: String
init(nuovoCodice: String) {
codice = nuovoCodice
}
}
var muccaPippo = Mucca.init(nuovoCodice: “01muccaPippo”)
var muccaGino = Mucca.init(nuovoCodice: “02muccaGino”)
A questo punto è facile dedurre che:
Le proprietà che ti servono obbligatoriamente alla creazione dell’oggetto vadano messe non opzionali (salvo alcuni casi), perché l’init ti assicurerà che lì ci sarà sempre un valore.
Mentre quelle che non ti servono subito le puoi lasciare opzionali.
class Mucca {
let codice: String
var latteProdotto: Double?
init(nuovoCodice: String) {
codice = nuovoCodice
}
}
var muccaPippo = Mucca.init(nuovoCodice: “01muccaPippo”)
muccaPippo.latteProdotto = 44
print(“mucca (muccaPippo.codice) ha prodotto (muccaPippo.latteProdotto!) litri di latte”)
// mucca 01muccaPippo ha prodotto 44.0 litri di latte
Classe come nuovo Tipo di Dato
Quando crei una classe, in realtà, stai creando anche un nuovo Tipo di Dato. Cioè come i Tipi di Dato che già hai incontrato (String, Double ecc – loro in realtà non sono delle classi) anche le classi ti permettono di definire il tipo ad una variabile.
class Mucca {}
var stallaMucche: [Mucca] = []
var muccaPippo: Mucca = Mucca.init()
var muccaGino: Mucca = Mucca.init()
stallaMucche.append(muccaPippo)
stallaMucche.append(muccaGino)
print(“nella stalla ci sono (stallaMucche.count) mucche”)
In questo semplice esempio ho definito un array di tipo [Mucca] cioè potrà contenere oggetti istanza della classe Mucca. In più, alle variabili muccaPippo e muccaGino ho definito in maniera esplicita il tipo di dato anche se non era strettamente necessario dato che già l’inizializzazione dell’oggetto gli assegna il tipo di dato Mucca.
Ad ogni modo è sempre una buona norma definire esplicitamente il tipo di dato quando si conosce a priori.
Metodi di una Classe con il linguaggio Swift
Un oggetto, come ribadito ad inizio lezione, può eseguire delle azioni. Camminare, mangiare, bere, correre, uccidere. Sono tutte azioni che accomunano o contraddistinguono gli oggetti.
Un cane abbaia, mentre un gatto miagola. Sono azioni simili, per certi versi, ma diverse nell’azione finale.
Le azioni, con il linguaggio Swift, vengono definite mediante le funzioni. Una classe può avere delle funzioni che permettono di far compiere delle azioni ad un oggetto.
Le azioni o funzioni, per un oggetto, prendono il nome di metodi.
Prendiamo sempre l’ultima classe creata. Quella della Mucca. Potremmo creare un metodo, cioè una funzione, che ci permetta di stampare la quantità di latte che ha prodotto. La chiameremo stampaQuantitàLatte e inseriremo un print della proprietà codice e della proprietà latteProdotto.
class Mucca {
let codice: String
var latteProdotto: Double?
init(nuovoCodice: String) {
codice = nuovoCodice
}
func stampaQuantitàLatte() {
print(“mucca (codice) ha prodotto (latteProdotto) litri di latte”)
}
}
Quando vorrai utilizzare il metodo potrai farlo mediante la notazione nomeOggetto.nomeMetodo() come una semplicissima funzione. Solo che, questa volta, la funzione partirà direttamente dall’oggetto.
var muccaPippo: Mucca = Mucca.init(nuovoCodice: “01muccaPippo”)
muccaPippo.latteProdotto = 44
muccaPippo.stampaQuantitàLatte()
muccaPippo.latteProdotto = 66
muccaPippo.stampaQuantitàLatte()
La cosa interessante è che i metodi interagiscono solo con le proprietà dell’oggetto che le invoca. Se creassi un’altra mucca, con una sua quantità di latte ed un codice, il metodo stamperà ovviamente solo i suoi valori perché devi pensare ad un oggetto come ad un’entità a se stante.
Metodi e parametri
Anche i metodi, come le normali funzioni, possono avere dei parametri.
Nel nostro esempio, potremmo creare un metodo che aggiunge una nuova quantità di latte alla proprietà latteProdotto.
class Mucca {
let codice: String
var latteProdotto: Double?
init(nuovoCodice: String) {
codice = nuovoCodice
}
func aggiungiQuantitàLatte(nuovaQuantità: Double) {
guard let quantitàPresente = latteProdotto else {
print(“ancora devi inizializzare la proprietà latteProdotto”)
return
}
latteProdotto! = quantitàPresente + nuovaQuantità
}
func stampaQuantitàLatte() {
print(“mucca (codice) ha prodotto (latteProdotto) litri di latte”)
}
}
Nel metodo aggiungiQuantitàLatte ho messo, prima di aggiungere la nuovaQuantità al latteProdotto, un controllo sulla presenza o meno di un valore nella proprietà latteProdotto dato che è opzionale. Successivamente ho aggiunto alla quantitàPresente la nuovaQuantità:
var muccaPippo: Mucca = Mucca.init(nuovoCodice: “01muccaPippo”)
muccaPippo.aggiungiQuantitàLatte(nuovaQuantità: 10)
muccaPippo.latteProdotto = 10
muccaPippo.stampaQuantitàLatte()
muccaPippo.aggiungiQuantitàLatte(nuovaQuantità: 20)
muccaPippo.aggiungiQuantitàLatte(nuovaQuantità: 10)
muccaPippo.stampaQuantitàLatte()
La parola chiave self
Diamo uno sguardo a questa semplice classe:
class Persona {
var nome: String
var cognome: String
init(nome: String, cognome: String) {
}
}
I parametri del costruttore init sono uguali, come sintassi, a quelli delle proprietà della classe.
Se adesso tu provassi ad assegnare il valore dei parametri ai valori delle proprietà della classe, noteresti il nascere di un errore perché i nomi sono uguali ed il compilatore non sa di preciso a quale ti stai riferendo.
init(nome: String, cognome: String) {
nome = nome // ERRORE: devo modificare la proprietà o il parametro?
cognome = cognome // ERRORE: devo modificare la proprietà o il parametro?
}
Per evitare il nascere di questi problemi, è stata creata una parola chiave che ti permette di riferirti alle proprietà o metodi della classe in cui ti trovi.
La parola chiave si chiama self e ti permette di accedere solo alle proprietà e metodi della classe. Grazie al self sei in grado di risolvere questo piccolo problema.
init(nome: String, cognome: String) {
self.nome = nome
self.cognome = cognome
}
Il self non serve solamente a questo scopo. Nel caso di progetti più vasti e articolati, utilizzare la parola chiave self ti fa subito capire che quella cosa che lo segue appartiene alla classe e non viene da un’altra parte del codice.
class Persona {
var nome: String
var cognome: String
init(nome: String, cognome: String) {
self.nome = nome
self.cognome = cognome
}
func stampaDati() {
print(self.nome)
print(self.cognome)
}
}
var giuseppe: Persona = Persona.init(nome: “Giuseppe”, cognome: “Sapienza”)
giuseppe.stampaDati()
Il mio consiglio è di utilizzare la parola chiave self sempre. Se stai modificando o utilizzando le proprietà o metodi della classe, utilizza sempre il self. In questo modo, quando andrai a ricontrollare il codice, capirai subito che quella proprietà/metodo la devi andare a cercare dentro la classe e non altrove.
A cosa serve la programmazione ad oggetti?
Fatto questo lungo excursus su come scrivere correttamente una semplice Classe è arrivato il momento di capire realmente a cosa servono.
Il motivo della loro nascita è abbastanza semplice. Si chiama semplificazione del codice e dei processi di sviluppo di un’applicazione.
Ma peppe, io tutta questa semplificazione non la sto vedendo, puoi essere più chiaro?
Per farti capire meglio l’utilizzo della programmazione ad oggetti con il linguaggio Swift, ti voglio fare dei semplici esempi di utilizzo reale.
Esempio 1. Applicazione per la gestione della lista della spesa
Hai una semplice applicazione che ti permette di salvare gli alimenti che devi acquistare al supermercato. Ogni cosa da acquistare è caratterizzata dalla categoria d’appartenenza (Dolci, Carne, Altro ecc) e dal nome dell’oggetto da comprare.
L’applicazione deve essere in grado di salvare tutti questi oggetti da comprare dentro una lista e di toglierli dalla lista per metterli nella lista dei già acquistati.
Vediamo un po’ come strutturare il progetto.
Hai bisogno di un qualcosa che ti descriva questi oggetti da comprare. A tal proposito puoi creare una classe, chiamata OggettoSpesa, formata dalle proprietà categoria e nome.
class OggettoSpesa {
var nome: String
var categoria: String
init(nome: String, categoria: String) {
self.nome = nome
self.categoria = nome
}
}
In questo modo, quando l’utente riempirà due campi di testo per definire la categoria ed il nome dell’oggetto da acquistare, l’applicazione creerà un nuovo OggettoSpesa.
var cotolette: OggettoSpesa = OggettoSpesa.init(nome: “cotolette”, categoria: “Carne”)
var gelato: OggettoSpesa = OggettoSpesa.init(nome: “gelato”, categoria: “Dolci”)
Senza la classe avresti avuto sicuramente delle variabili sparse e non collegate logicamente, oppure delle tuple che a lungo andare avrebbero confuso di tantissimo il progetto.
Adesso ci serve un contenitore dove poter mettere questi oggetti da acquistare.
Potremmo creare un ulteriore oggetto che ci permetta di gestire una lista di oggetti da acquistare ed una lista di oggetti acquistati. Cioè, invece di avere dei semplici array sparsi nel codice come questi:
var oggettiDaAcquistare: [OggettoSpesa]
var oggettiAcquistati: [OggettoSpesa]
Li metteremo all’interno di una classe chiamata GestoreListaSpesa che ci permetterà di interagire in maniera semplificata con questi array:
class GestoreListaSpesa {
var oggettiDaAcquistare: [OggettoSpesa]
var oggettiAcquistati: [OggettoSpesa]
init() {
self.oggettiAcquistati = []
self.oggettiDaAcquistare = []
}
}
Qui arriva il bello della programmazione ad oggetti. La complessità del progetto adesso si sposta tutta dentro la classe.
Potremmo creare un metodo della classe che ci permetta di poter aggiungere il nuovo oggetto alla lista oggettiDaAcquistare:
func aggiungi(oggetto: OggettoSpesa) {
self.oggettiDaAcquistare.append(oggetto)
}
In modo tale che quando si utilizzerà, verrà più semplice ed immediato l’inserimento dell’oggetto da acquistare nella lista opportuna:
let gestoreLista: GestoreListaSpesa = GestoreListaSpesa.init()
var cotolette = OggettoSpesa.init(nome: “cotolette”, categoria: “Carne”)
var gelato = OggettoSpesa.init(nome: “gelato”, categoria: “Dolci”)
gestoreLista.aggiungi(oggetto: cotolette)
gestoreLista.aggiungi(oggetto: gelato)
Idem per la rimozione dell’oggetto dalla lista. Puoi creare un nuovo metodo che, dato l’inserimento del nome dell’oggetto acquistato, rimuova l’elemento dall’array e lo passi all’interno dell’array oggettiAcquistati. Aggiungi il seguente metodo alla classe GestoreListaSpesa:
func rimuovi(nome: String) {
var indice = 0
for elemento in self.oggettiDaAcquistare {
if elemento.nome == nome {
self.oggettiAcquistati.append(elemento)
self.oggettiDaAcquistare.remove(at: indice)
}
indice += 1
}
}
Il codice trova l’elemento nella lista degli oggettiDaAcquistare, lo aggiunge agli oggettiAcquistati ed infine rimuove l’elemento dall’array oggettiDaAcquistare (con la funzione .remove(at: Int) che vuole l’indice della posizione dell’elemento nell’array).
[su_spoiler title=”Tutto il Codice dell’esercizio” style=”fancy”]
class OggettoSpesa {
var nome: String
var categoria: String
init(nome: String, categoria: String) {
self.nome = nome
self.categoria = nome
}
}
class GestoreListaSpesa {
var oggettiDaAcquistare: [OggettoSpesa]
var oggettiAcquistati: [OggettoSpesa]
init() {
self.oggettiAcquistati = []
self.oggettiDaAcquistare = []
}
func aggiungi(oggetto: OggettoSpesa) {
self.oggettiDaAcquistare.append(oggetto)
}
func rimuovi(nome: String) {
var indice = 0
for elemento in self.oggettiDaAcquistare {
if elemento.nome == nome {
self.oggettiAcquistati.append(elemento)
self.oggettiDaAcquistare.remove(at: indice)
}
indice += 1
}
}
}
let gestoreLista: GestoreListaSpesa = GestoreListaSpesa.init()
var cotolette = OggettoSpesa.init(nome: “cotolette”, categoria: “Carne”)
var gelato = OggettoSpesa.init(nome: “gelato”, categoria: “Dolci”)
gestoreLista.aggiungi(oggetto: cotolette)
gestoreLista.aggiungi(oggetto: gelato)
gestoreLista.rimuovi(nome: “cotolette”)
print(“hai già acquistato (gestoreLista.oggettiAcquistati.count) oggetti”)
print(“devi ancora acquistare (gestoreLista.oggettiDaAcquistare.count) oggetti”)
[/su_spoiler]