2.1 Concetti base di OOP
Alla base dell'approccio orientato agli oggetti nella programmazione c'è il paradigma OOP. Questo quando tutte le entità del programma sono considerate come oggetti. Gli oggetti sono dati + metodi/funzioni che fanno qualcosa su questi dati.
Si può anche dire che un oggetto ha uno stato e un comportamento. Lo stato di un oggetto è dato dai suoi dati, i quali sono memorizzati nelle sue variabili interne. Il comportamento di un oggetto — è l'insieme di tutte le azioni eseguite dai suoi metodi.
Ci sono migliaia di oggetti in un programma medio, milioni in uno grande. Per ridurre il caos, è stato deciso di organizzare gli oggetti in classi e le classi in una certa gerarchia.
È interessante! Il concetto di gerarchia delle classi è stato preso dal mondo animale, dove ci sono esseri umani, che sono primati, che sono mammiferi, e così via.
Ogni oggetto ha una classe con cui è creato. Da una parte, una classe è un certo prototipo di oggetto, dall'altra è un oggetto indipendente con le sue peculiarità (ne parleremo più avanti).
Per comprendere meglio il concetto di classi, dai un'occhiata alle seguenti definizioni:
Classe:
La classe è un modello o schema per creare oggetti, che definisce attributi e metodi, caratteristici di tutti gli oggetti di questo tipo. Le classi permettono di organizzare dati e funzioni che lavorano con questi dati in un'unica entità.
Oggetto:
Un oggetto è un'istanza di una classe. Ogni oggetto possiede stato (determinato dagli attributi) e comportamento (determinato dai metodi).
Incapsulamento:
L'incapsulamento consiste nel nascondere l'implementazione interna di una classe e nel fornire un'interfaccia per interagire con gli oggetti di questa classe. Questo aiuta a proteggere i dati e a gestire l'accesso a essi.
Ereditarietà:
L'ereditarietà permette a una classe (figlia) di ereditare attributi e metodi di un'altra classe (genitore). Questo favorisce il riutilizzo del codice e ne semplifica la manutenzione.
Polimorfismo:
Il polimorfismo consente di usare un'unica interfaccia per lavorare con oggetti di classi diverse. Questo si realizza tramite l'override dei metodi nelle classi figlie, che ereditano dai genitori.
Astrattizzazione:
L'astrattizzazione consiste nel mettere in risalto le caratteristiche comuni degli oggetti e nel creare classi che rappresentano queste caratteristiche comuni. Questo aiuta a semplificare sistemi complessi e migliorarne la comprensibilità.
Se hai capito almeno la metà — ottimo. Poi analizzeremo ciascuno di questi punti più in dettaglio.
2.2 Astrattizzazione
Un buon esempio di astrazione nella vita reale è la descrizione delle posizioni in un'azienda o organizzazione. Il titolo di lavoro è una cosa, ma i compiti di ciascuna posizione specifica sono un'altra cosa.
Immagina di progettare la struttura della tua futura azienda. Puoi distribuire i compiti del segretario, "spargendoli" su diverse altre posizioni. Puoi dividere il ruolo di amministratore delegato in diversi ruoli indipendenti: direttore finanziario, direttore tecnico, direttore marketing, direttore delle risorse umane. O, ad esempio, unire le posizioni di office manager e reclutatore in una sola.
Dal punto di vista della programmazione, l'astrazione è, per così dire, una divisione corretta del programma in oggetti. Di solito è possibile rappresentare qualsiasi programma grande come un insieme di oggetti interagenti in decine di modi diversi. L'astrazione permette di selezionare le caratteristiche principali e tralasciare quelle secondarie.
L'astrazione è come una strategia nella guerra. Una cattiva strategia — e nessuna tattica geniale potrà già risolvere la situazione.
2.3 Incapsulamento
L'obiettivo dell'incapsulamento è migliorare la qualità dell'interazione degli elementi semplificando la loro interazione.
E il modo migliore per semplificare qualcosa è nascondere tutto ciò che è complesso agli occhi esterni. Ad esempio, se ti mettono nella cabina di un Boeing, non capirai subito come pilotarlo:
D'altra parte, per i passeggeri dell'aereo tutto sembra più semplice: compri il biglietto, sali sull'aereo, decolli e atterri. Puoi facilmente volare da continente a continente, possedendo solo le abilità "comprare un biglietto" e "salire sull'aereo". Tutte le complessità come la preparazione dell'aereo al volo, il decollo, l'atterraggio e le varie situazioni di emergenza sono nascoste a noi. Senza parlare della navigazione satellitare, pilota automatico e centri di controllo negli aeroporti. E questo ci semplifica la vita.
Dal punto di vista della programmazione, l'incapsulamento è "nascondere l'implementazione". Mi piace questa definizione. La nostra classe può contenere centinaia di metodi e realizzare comportamenti molto complessi in varie situazioni. Ma possiamo nascondere tutti i suoi metodi agli occhi indiscreti (mettere i loro nomi tra "__" su entrambi i lati), e per interagire con altre classi lasciare solo un paio-tre di metodi. Quindi tutte le altre classi del nostro programma vedranno in questa classe solo tre metodi e chiameranno proprio questi. E tutte le complessità saranno nascoste all'interno della classe, come la cabina di pilotaggio dai felici passeggeri.
2.4 Ereditarietà
L'ereditarietà ha due lati. Il lato programmatico e il lato della vita reale. Dal punto di vista della programmazione, l'ereditarietà — è una relazione speciale tra due classi. Ma è molto più interessante sapere cosa sia l'ereditarietà dal punto di vista della vita reale.
Se dovessimo creare qualcosa nella vita reale, ci sono due vie:
- creare l'oggetto necessario da zero, investendo una marea di tempo e sforzi;
- creare l'oggetto necessario basandoci su qualcosa che esiste già.
La strategia più ottimale sembra essere: prendere una buona soluzione esistente, migliorarla un po', adattarla ai propri bisogni e usarla.
Se seguiamo l'evoluzione dell'uomo, scopriremo che dal momento della nascita della vita sul pianeta sono passati miliardi di anni. E se immaginiamo che l'uomo si sia evoluto dalla scimmia (basandosi sulla scimmia), sono passati solo un paio di milioni di anni. La creazione da zero — richiede più tempo. Molto più tempo..
Anche nella programmazione c'è la possibilità di creare una classe basandosi su un'altra. La nuova classe diventa discendente (erede) di quella già esistente. Questo è molto vantaggioso quando c'è una classe che contiene il 80%-90% dei dati e dei metodi di cui abbiamo bisogno. Dichiariamo semplicemente la classe appropriata come genitore della nostra nuova classe, quindi nella nuova classe appaiono automaticamente tutti i dati e i metodi della classe genitore. Comodo, vero?
2.5 Polimorfismo
Il polimorfismo è un concetto della programmazione. Descrive la situazione in cui dietro un'unica interfaccia ci sono implementazioni diverse. Se cerchiamo analogie nella vita reale, una di queste potrebbe essere il processo di guida di un veicolo.
Se una persona può guidare un camion, allora può mettersi anche al volante di un'ambulanza o di un'auto sportiva. La persona può guidare una macchina indipendentemente dal tipo di veicolo, perché tutti hanno la stessa interfaccia di controllo: volante, pedali e leva del cambio. L'interno delle macchine è diverso, ma tutte hanno lo stesso interfaccia di controllo.
Tornando alla programmazione, il polimorfismo permette di interagire in modo uniforme con oggetti di classi diverse (di solito che hanno un antenato comune) – una caratteristica difficile da sopravvalutare. Il suo valore cresce con l'aumentare della dimensione del programma.
OOP — sono dei principi. Leggi interne. Ciascuno di essi ci limita in qualcosa, offrendoci in cambio grandi vantaggi, quando il programma cresce fino a dimensioni considerevoli. I quattro principi di OOP sono come le quattro gambe di una sedia. Se ne togli solo una, e l'intero sistema diventerà instabile.
GO TO FULL VERSION