2.1 Concetti di base della OOP
Alla base dell'approccio orientato agli oggetti nella programmazione c'è il paradigma OOP. È quando tutte le entità del programma sono considerate come oggetti. Gli oggetti sono dati + metodi/funzioni che fanno qualcosa con questi dati.
Si può anche dire che un oggetto ha uno stato e un comportamento. Lo stato di un oggetto è fornito dai suoi dati, che sono memorizzati nelle sue variabili interne. Il comportamento di un oggetto è la somma di tutte le azioni che i suoi metodi eseguono.
In un programma medio ci sono migliaia di oggetti, in uno grande milioni. Per ridurre il caos, si è deciso di organizzare gli oggetti in classi, e le classi di oggetti in una certa gerarchia.
Ogni oggetto ha una classe con cui è generato. Da un lato, una classe è una sorta di modello di oggetto, dall'altro è un oggetto indipendente con le sue caratteristiche (di cui parleremo più avanti).
Per comprendere meglio il concetto di classi, dai un'occhiata alle seguenti definizioni:
Classe — è un modello o uno 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 tutto unico.
Oggetto — istanza di una classe. Ogni oggetto ha uno stato (determinato dagli attributi) e un comportamento (determinato dai metodi).
Incapsulamento — consiste nel nascondere l'implementazione interna di una classe e nel fornire un'interfaccia per l'interazione con gli oggetti di quella classe. Questo aiuta a proteggere i dati e a gestire l'accesso ad essi.
Ereditarietà — consente a una classe (figlia) di ereditare attributi e metodi da un'altra classe (genitore). Questo facilita il riutilizzo del codice e la sua manutenzione.
Polimorfismo — consente di utilizzare un'interfaccia unica per lavorare con oggetti di classi diverse. Ciò è raggiunto attraverso l'override dei metodi nelle classi figlie, che ereditano dai genitori.
Astrattezza — consiste nell'evidenziare le caratteristiche comuni degli oggetti e nel creare classi che rappresentano tali caratteristiche comuni. Questo aiuta a semplificare sistemi complessi e a migliorarne la comprensibilità.
Se hai capito almeno la metà, ottimo. Più avanti analizzeremo ciascuno di questi punti in dettaglio.
2.2 Astrattezza
Un buon esempio di astrazione nella vita reale è la descrizione delle posizioni in un'azienda o organizzazione. Il nome della posizione è una cosa, ma i compiti di ciascuna posizione specifica sono un'altra cosa.
Immagina di progettare la struttura della tua futura azienda. Puoi dividere i compiti di un segretario: "spargerli" su altre diverse posizioni. Puoi dividere la posizione di direttore esecutivo in diverse posizioni indipendenti: direttore finanziario, direttore tecnico, direttore marketing, direttore delle risorse umane. Oppure, ad esempio, puoi unire le posizioni di office manager e reclutatore in una sola.
Dal punto di vista della programmazione, l'astrazione è, diciamo, la corretta suddivisione del programma in oggetti. Di solito qualsiasi grande programma può essere rappresentato in decine di modi come oggetti interagenti. L'astrazione consente di scegliere le caratteristiche principali e di omettere quelle secondarie.
L'astrazione è come una strategia nel campo militare. Una cattiva strategia, e nessuna tattica geniale potrà correggere la situazione.
2.3 Incapsulamento
L'obiettivo dell'incapsulamento è migliorare la qualità dell'interazione delle cose semplificandole.
E il modo migliore per semplificare qualcosa è nascondere tutto ciò che è complesso agli occhi degli estranei. Ad esempio, se ti mettono nella cabina di un Boeing, non capirai subito come gestirlo:
D'altra parte, per i passeggeri dell'aereo tutto sembra più semplice: hai comprato il biglietto, sei salito sull'aereo, sei decollato e sei atterrato. Puoi facilmente volare da un continente all'altro, possedendo solo le abilità di "comprare il biglietto" e "salire sull'aereo". Tutte le complessità nella preparazione dell'aereo per il volo, nel decollo, nell'atterraggio e nelle varie situazioni non ordinarie sono nascoste da noi. Per non parlare della navigazione satellitare, del pilota automatico e dei centri di controllo negli aeroporti. E questo semplifica la nostra 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 implementare un comportamento molto complesso in varie situazioni. Ma possiamo nascondere agli occhi degli estranei tutti i suoi metodi (incorniciando i loro nomi con "__" su entrambi i lati), e per l'interazione con altre classi lasciare solo un paio di metodi. Allora tutte le altre classi del nostro programma vedranno in questa classe solo tre metodi e chiameranno proprio quelli. E tutta la complessità sarà nascosta all'interno della classe, come la cabina dei piloti dai felici passeggeri.
2.4 Ereditarietà
L'ereditarietà ha due aspetti: l'aspetto della programmazione e l'aspetto della vita reale. Dal punto di vista della programmazione, l'ereditarietà è un rapporto speciale tra due classi. Ma è molto più interessante cos'è l'ereditarietà dal punto di vista della vita reale.
Se dovessimo creare qualcosa nella vita reale, avremmo due soluzioni:
- creare ciò di cui abbiamo bisogno da zero, spendendo un sacco di tempo e sforzi;
- creare ciò di cui abbiamo bisogno basandoci su qualcosa di già esistente.
La strategia più ottimale sembra essere questa: prendiamo una buona soluzione esistente, la miglioriamo un po', la adattiamo ai nostri bisogni e la usiamo.
Se tracciamo la storia dell'origine dell'uomo, scopriremo che miliardi di anni sono passati da quando la vita ha avuto origine sul pianeta. E se immaginiamo che l'uomo sia emerso dalla scimmia (sulla base della scimmia), sono passati solo un paio di milioni di anni. Creare da zero è più lungo. Molto più lungo.
Anche nella programmazione c'è la possibilità di creare una classe basata su un'altra. La nuova classe diventa discendente (erede) di una classe già esistente. Questo è molto vantaggioso quando c'è una classe che contiene l'80%-90% dei dati e metodi di cui abbiamo bisogno. Dichiariamo semplicemente una classe appropriata come genitore della nostra nuova classe, e quindi nella nuova classe compaiono automaticamente tutti i dati e i metodi della classe genitore. Comodo, vero?
2.5 Polimorfismo
Il polimorfismo è un concetto nell'ambito della programmazione. Descrive una situazione in cui dietro un'unica interfaccia si nascondono diverse implementazioni. Se proviamo a cercare i suoi analoghi nella vita reale, uno di tali analoghi sarà il processo di guida di un'auto.
Se una persona può guidare un camion, può essere messa anche al volante di un'ambulanza o di un'auto sportiva. Una persona può guidare un'auto indipendentemente dal tipo di auto, perché tutte hanno la stessa interfaccia di guida: volante, pedali e cambio. Il meccanismo interno delle auto è diverso, ma tutte hanno la stessa interfaccia di guida.
Se torniamo alla programmazione, il polimorfismo consente di trattare in modo uniforme gli oggetti di diverse classi (di solito aventi un antenato comune) — una cosa che è difficile da sopravvalutare. Il suo valore aumenta con la dimensione del programma.
OOP — sono principi. Leggi interne. Ciascuno di essi ci limita in qualche modo, offrendoci in cambio grandi vantaggi quando il programma cresce a dimensioni elevate. Quattro principi di OOP — sono come le quattro gambe di una sedia. Rimuovi anche solo uno, e l'intero sistema diventerà instabile.
GO TO FULL VERSION