Vai al contenuto

ūüď£ sulla piattaforma e-learning di Codice Inutile sono disponibili i miei corsi: Introduzione alla programmazione e ūüÜē Introduzione all'Arte Generativa

OOP: Classi e oggetti, parte 2

ūüď¨ Per rimanere aggiornato sui prossimi articoli e su altri contenuti di creative coding, puoi iscriverti a questa newsletter. Per farlo √® sufficiente inserire il tuo indirizzo e-mail nel campo qui sotto.

Puoi leggere l'archivio qui. Powered by TinyLetter

Ripartiamo da dove ci siamo fermati con l’ultimo post: abbiamo scritto la nostra prima classe e abbiamo creato il nostro primo oggetto. Nell’approfondimento di oggi¬†su classi e oggetti parleremo di alcune questioni importanti: come mantenere pulito il codice quando si lavora con le classi, come¬†passare parametri al constructor e, infine,¬†come creare¬†n oggetti.

Operazione pulizia: utilizzare le Tab

Car myCar;

void setup() {
  size(500, 500);
  myCar = new Car();
}

void draw() {
  background(255);
  myCar.display();
  myCar.move();
}


class Car {
  // Dati
  int carLength;
  int xPos;
  int yPos;
  int speed;

  // Constructor
  Car() {
    carLength = 10;
    xPos = 0;
    yPos = 200;
    speed = 1;
  }

  // Metodi
  void display() {
    rectMode(CENTER);
    fill(0);
    rect(xPos, yPos, carLength, 10);
  }

  void move() {
    xPos = xPos + speed;
  }
}

Il nostro codice ora √® lungo 40 righe in tutto e la classe¬†Car¬†occupa la maggior parte dello spazio. Immaginate per un secondo di avere tra le mani un programma molto complesso, che utilizza tante tipologie di oggetti differenti. Sarebbe davvero problematico andare a cercare di volta in volta scorrendo in su e in gi√Ļ la porzione di codice¬†che si riferisce all’oggetto che vogliamo utilizzare.

Per fortuna Processing viene in nostro aiuto con una funzione molto comoda: le Tab.

Crea una nuova Tab su Processing

Cliccando sulla freccia che punta in basso accanto al nome del nostro sketch, si aprir√† un menu con diverse opzioni. Clicchiamo su New Tab (su Mac la scorciatoia √® ‚áß‚ĆėN). A questo punto il programma ci chieder√† il¬†nome che vogliamo dare alla nostra tab; dovendo spostare l√¨ la classe Car, utilizziamo lo stesso nome sempre con l’iniziale maiuscola.

Una volta cliccato su OK, l’editor creer√† un altro file .pde che verr√† aggiunto automaticamente alla cartella dove abbiamo salvato il nostro sketch (la scorciatoia per vedere velocemente la cartella √®¬†‚ĆėK).

Sketch Folder

A questo punto¬†facciamo copia-incolla del codice della classe nella nuova tab e proviamo a cliccare¬†Run.¬†Il programma dovrebbe aprirsi e funzionare esattamente come prima con la differenza che, ora, il codice del file principale ‚Äď quello nominato sketch_160115a.pde in questo esempio ‚Äď √® molto pi√Ļ breve e pulito.

Passare parametri al constructor

La nostra classe Car¬†√® ora funzionale e pulita ma √® ancora migliorabile: ho scritto pi√Ļ volte dell’importanza di utilizzare le variabili invece di parametri hard-coded ma il constructor che abbiamo ora generer√† sempre oggetti con le stesse caratteristiche: la lunghezza sar√† sempre pari a 10 pixel, la posizione di partenza sar√† x = 0 e y = 200 e la velocit√† pari a 1.

La buona notizia √® che possiamo inserire nella nostra classe pi√Ļ constructor mantenendo una sintassi simile a quella che gi√† conosciamo; dobbiamo solo aggiungere, all’interno della parentesi tonda, le variabili che decidiamo di passare ai nostri oggetti.

In questo primo esempio, aggiungiamo la possibilità di modificare la lunghezza della nostra macchina.

class Car {
  // Dati
  int carLength;
  int xPos;
  int yPos;
  int speed;

  // Constructor
  Car() {
    carLength = 10;
    xPos = 0;
    yPos = 200;
    speed = 1;
  }
  
  // Secondo Constructor
  Car(int tempCarLength) {
    carLength = tempCarLength;
    xPos = 0;
    yPos = 200;
    speed = 1;
  }

  // Metodi
  void display() {
    rectMode(CENTER);
    fill(0);
    rect(xPos, yPos, carLength, 10);
  }

  void move() {
    xPos = xPos + speed;
  }
}

In questa nuova versione ho aggiunto alla classe un nuovo constructor che accetta un solo parametro di tipo integer. Quando il parametro viene passato devo utilizzare una variabile temporanea, motivo per cui ho inserito la variabile¬†tempCarLength.¬†La variabile temporanea dovr√† poi essere assegnata alla variabile relativa all’interno del costructor:¬†carLength = tempCarLength.

Questo è il momento in cui è necessario diventare molto ordinati e scrupolosi con i nomi che diamo alle nostre variabili, il rischio è di complicarci inutilmente la vita.

Ora che abbiamo un nuovo constructor, torniamo al nostro sketch principale e modifichiamo l’inizializzazione da cos√¨:

myCar = new Car();

a così:

myCar = new Car(25);

Proviamo a lanciare il nostro sketch.

Variabili al constructor

Il nostro rettangolo sarà ora lungo 25 pixel.

Cosa succede se proviamo a passare al nostro constructor una variabile di tipo float?

myCar = new Car(25.7);

Ovviamente ci verr√† restituito un errore:¬†The constructor “Car(float)” does not exist.

Constructor, constructor, constructor

Il prossimo passaggio sar√† creare tanti constructor quanti sono i dati che il nostro oggetto contiene: nel nostro programma vorremmo, ad esempio, decidere anche la posizione x e y di partenza oltre, ovviamente, diversi valori di velocit√†. Possiamo fare anche un passaggio in pi√Ļ: aggiungiamo una nuova variabile che contenga anche il colore della macchina.

Vi invito a provare a modificare la classe per conto vostro e di non guardare subito il codice qui sotto. In questo modo capirete se, effettivamente, vi è tutto chiaro.

class Car {
  // Dati
  int carLength;
  float xPos;
  int yPos;
  float speed;
  color c;

  // Constructor
  Car() {
    carLength = 10;
    xPos = 0;
    yPos = 200;
    speed = 1;
    c = color(0);
  }
  
  // Secondo Constructor
  Car(int tempCarLength) {
    carLength = tempCarLength;
    xPos = 0;
    yPos = 200;
    speed = 1;
    c = color(0);
  }
  
  // Secondo Constructor
  Car(int tempCarLength, int tempXPos) {
    carLength = tempCarLength;
    xPos = tempXPos;
    yPos = 200;
    speed = 1;
    c = color(0);
  }
  
  Car(int tempCarLength, int tempXPos, int tempYPos) {
    carLength = tempCarLength;
    xPos = tempXPos;
    yPos = tempYPos;
    speed = 1;
    c = color(0);
  }
  
  Car(int tempCarLength, int tempXPos, int tempYPos, float tempSpeed) {
    carLength = tempCarLength;
    xPos = tempXPos;
    yPos = tempYPos;
    speed = tempSpeed;
    c = color(0);
  }
  
  Car(int tempCarLength, int tempXPos, int tempYPos, float tempSpeed, color tempColor) {
    carLength = tempCarLength;
    xPos = tempXPos;
    yPos = tempYPos;
    speed = tempSpeed;
    c = tempColor;
  }

  // Metodi
  void display() {
    rectMode(CENTER);
    fill(c);
    rect(xPos, yPos, carLength, 10);
  }

  void move() {
    xPos = xPos + speed;
  }
}

Sottolineo un paio di cose:

  1. Ho aggiornato la nostra classe senza toccare il codice del nostro sketch principale. Questa è la potenza della programmazione orientata agli oggetti: modificare porzioni di codice senza andare a intaccare le altre parti che compongono il programma.
  2. Ho utilizzato per la prima volta il datatype color. Non è niente di difficile ma, dal momento che non ho intenzione di spiegare come funziona in questo post, vi invito a leggere il manuale.
  3. Ho cambiato il datatype di speed in float e sono stato costretto a cambiarlo anche per xPos dal momento che nella funzione move() i due parametri vengono sommati. Se avessi lasciato xPos come int, Processing mi avrebbe restituito un errore.

Ora che la nostra classe √® completa, possiamo sbizzarrirci a passare quanti parametri vogliamo, l’importante¬†√® ¬†ricordarsi l’ordine dei parametri e i tipi di dati accettati:

myCar = new Car(25, 50, 200, 0.5, color(255, 255, 0));

Il bello è che possiamo utilizzare anche delle funzioni per passare i parametri. Vi ricordate di random?

myCar = new Car(25, 50, 200, random(5), color(random(255), random(255), random(255)));

Creiamo n oggetti

Nell’introduzione agli oggetti avevo specificato che un punto di forza dell’OOP √® poter creare pi√Ļ istanze dello stesso oggetto.¬†Come possiamo, dunque, creare 3¬†macchine?

Riprendiamo in mano il nostro sketch principale e aggiungiamo altre due variabili di tipo Car chiamate myCar2 e myCar3, in setup() creiamo due nuove istanze degli oggetti e, in draw() ci assicuriamo di richiamare, per ciascuna, i metodi display() e move().

Car myCar, myCar2, myCar3;

void setup() {
  size(500, 500);
  myCar = new Car(25, 50, 200, random(5), color(random(255), random(255), random(255)));
  myCar2 = new Car(int(random(5, 50)), int(random(width)), int(random(height)), random(5), color(random(255), random(255), random(255)));
  myCar3 = new Car(int(random(5, 50)), int(random(width)), int(random(height)), random(5), color(random(255), random(255), random(255)));
}

void draw() {
  background(255);
  myCar.display();
  myCar.move();
  
  myCar2.display();
  myCar2.move();
  
  myCar3.display();
  myCar3.move();
}

Premiamo su Run:

Abbiamo creato 3 oggetti Car

Fino a qui tutto bene, ma, a questo punto la domanda che sorge spontanea √®: se volessimo creare¬†decine o¬†centinaia¬†di oggetti? Dovremmo impazzire con il copia-incolla. Chiaramente esiste una soluzione pi√Ļ comoda: utilizzare gli array che, per l’appunto, saranno il prossimo argomento che tratteremo.

Compiti a casa

√ą arrivato il momento di dimostrare di aver capito come funziona la programmazione ad oggetti: riprendete in mano l’esercizio Bouncing Ball (parte 1 e parte 2) e modificate il codice per renderlo object-oriented.

Articolo precedente

OOP: Sintassi di classi e oggetti

Articolo successivo

Esercizio: Bouncing Ball, parte 3 (OOP)

Unisciti alla discussione

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.