Guardando in profondità il polimorfismo
Quando ho iniziato questa serie, ho parlato dei quattro pilastri della programmazione orientata agli oggetti. Ciascuno di questi argomenti è elencato e collegato di seguito.
A questo punto, normalmente vorrei iniziare a passare al prossimo argomento. Prima di farlo, però, vorrei dedicare un altro post all’esplorazione del concetto di polimorfismo.
Nella mia carriera, finora, ho visto pochi argomenti dare a coloro che si avvicinano alla programmazione orientata agli oggetti più confusione e problemi rispetto al polimorfismo. Quindi vorrei discuterne un po’ più in profondità nel contesto della programmazione orientata agli oggetti e al di fuori di qualsiasi framework o applicazione particolare (come WordPress).
In questo post, farò una rapida rassegna di ciò di cui abbiamo discusso finora, quindi tornerò al polimorfismo.
Approfondimento sul polimorfismo
Innanzitutto, come accennato, voglio rivedere rapidamente ciò che è stato discusso finora, soprattutto se hai sorvolato su uno qualsiasi dei post precedenti.
Non preoccuparti: niente di seguito divaga nel codice. Invece, definisce semplicemente i termini che abbiamo usato in modo da avere un’idea di ciò a cui mi riferisco ogni volta che vedi la parola spuntare in questa serie.
Astrazione
Astraiamo l’idea di qualcosa in una classe. Invece, astrarremo le idee nelle loro classi. E qui c’è un’idea chiave: una classe dovrebbe rappresentare un sostantivo.
Incapsulamento
Incapsulamento è in realtà solo una parola "grande" che si riferisce all’idea di gestire le proprie responsabilità (o tenere traccia dei propri dati).
Eredità
L’ereditarietà si riferisce all’idea che una classe, sebbene abbia una propria implementazione, eredita letteralmente proprietà, funzioni e implementazione generale da una classe padre.
Polimorfismo
Il polimorfismo ci consente di fare riferimento a una classe di un tipo quando può essere dichiarata come un altro tipo.
Detto questo, è qui che penso che le cose possano diventare un po’ più complicate. Nei post precedenti, ho fornito una serie di esempi di codice diversi (e ti esorto a guardarli indietro).
Ma nel post di oggi, cercherò di esplorare un po’ più a fondo l’idea sia nella spiegazione che nel codice.
Relativo all’eredità
Se non è evidente a questo punto, il polimorfismo è fortemente correlato all’ereditarietà. Pensala in questo modo: se una classe eredita proprietà e metodi da un’altra classe, può "stare al posto" della classe genitore.
Ciò significa che se hai qualcosa come una classe Content e poi hai due sottoclassi, una che è un Post e l’altra che è una Pagina, puoi creare un’istanza della classe usando il tipo di riferimento del contenuto.t
Ma in fase di esecuzione, sarà effettivamente un’istanza del tipo Post. Ha senso? Ecco del codice come esempio.
Innanzitutto, inizieremo con la definizione di una classe Content :
<?php
class Content {
protected $title;
protected $content;
protected $metadata;
public function __construct()
{
$this->title = "Hello World!";
$this->content = "This is a sample piece of content.";
$this->metadata = "<This is the metadata of the post.>";
}
public function getTitle()
{
return $this->title;
}
public function getContent()
{
return $this->content;
}
public function getMetadata()
{
return $this->metadata;
}
}
Ha le proprietà generali che probabilmente ti aspetteresti: titolo, contenuto e metadati. Certo, queste proprietà sono solo stringhe ma potrebbero essere strutture di dati più elaborate in una situazione del mondo reale.
Ora diamo un’occhiata a un post :
<?php
class Post extends Content {
private $author;
public function __construct() {
parent::__construct();
$this->author = "Tom McFarlin";
}
public function getAuthor()
{
return $this->author;
}
}
Cosa succede, quindi, se chiami un metodo sulla classe Post, come getTitle, che non esiste ma esiste nella classe Content? Quindi, a causa dell’ereditarietà, cercherà il metodo in Post, non lo troverà, quindi inizierà a cercarlo in Content.
Se viene trovato, lo eseguirà.
Allo stesso modo, possiamo fare qualcosa del genere con la classe Page e i dati del contenuto. Innanzitutto, istanziamo la classe base, quindi impostiamo proprietà specifiche per Page. In questo caso, sarà una categoria.
<?php
class Page extends Content {
private $category;
public function __construct() {
parent::__construct();
$this->category = "Articles";
}
public function getCategory()
{
return $this->category;
}
}
Ora, quando eseguiamo il codice, possiamo iniziare con il contenuto:
<?php
$content = new Content();
echo $content->getTitle();
Nota che questo sembra che ci aspetteremmo dal momento che abbiamo un titolo e abbiamo contenuto. Diamo un’occhiata anche al Post :
<?php
// These will work because they reside in the Content base class.
$post = new Post();
echo $post->getAuthor();
echo $post->getTitle();
Funziona perché abbiamo un autore, ma abbiamo anche un titolo perché risiede in Contenuto. Ma se proviamo a chiamare getAuthor su un’istanza di Post?
<?php
// These will work because they reside in the Content base class.
$post = new Post();
echo $post->getAuthor();
echo $post->getTitle();
Otterremo un errore perché il metodo non risiede in quella classe.
Allora cosa dobbiamo fare? Dal momento che non abbiamo tipi forti in PHP, non possiamo eseguirne il cast su un tipo diverso.
Ci sono modelli di progettazione per questo, di cui parlerò in una serie di post futuri, ma PHP non lo consente facilmente come alcuni altri linguaggi (come C# o Java).
Domande sul polimorfismo
Si spera che il codice precedente ti dia un’idea di come un tipo concreto come un Post o una Pagina può avere implicitamente le proprietà e i metodi della sua classe base, Content, usata in fase di esecuzione.
Ma solleva anche alcune domande, no? Per esempio:
- Perché il polimorfismo è utile? Alla fine, è una questione di flessibilità. Cioè, puoi scrivere un tipo di Contenuto generico ma poi creare un Post o una Pagina come abbiamo visto sopra. Questo ci offre quindi tutti i vantaggi della classe Content e allo stesso tempo ci dà anche la specificità della classe Post, ad esempio.
- Questo sembra essere più confuso che flessibile. In un certo senso, è fonte di confusione perché il codice richiede un po’ di traccia. Cioè, potresti iniziare nella classe Post e dover cercare ciò che offre la classe Contenuto . D’altra parte, rende anche molto facile introdurre una nuova sottoclasse di contenuto e quindi usarla quando è più adatta in fase di esecuzione.
Per quanto riguarda le superclassi e le sottoclassi, è qui che entra in gioco un solido IDE.
È sempre bello avere un editor che ti piace, certo, ma averne uno in grado di determinare intuitivamente qual è la classe genitore, qual è la classe base, ecc., può aiutare molto a tracciare, eseguire il debug, seguire e scrivere nuovi codice.
Ma questo è il contenuto di un altro post che verrà dopo aver parlato di design pattern.

