Pensiero Orientato agli Oggetti
Orientato agli Oggetti è una frase d'effetto. Dire che qualcosa è object oriented, può farvi apparire più intelligenti. Ruby pretende di essere un linguaggio di scripting orientato agli oggetti; ma cosa significa esattamente "object oriented" ?
Ci sono state molte risposte a questa domanda, e quasi tutte probabilmente dicono più o men la stessa cosa. Invece di riassumere velocemente, pensiamo per un momento al paradigma di programmazione tradizionale.
Tradizionalmente, un problema di programmazione è attaccato creando inizialmente una sorta di rappresentazione dei dati , e procedure che operino con quei dati. Con questo modello, i dati sono inerti, passivi, e non sono d'aiuto; esso si appoggia su un largo corpo procedurale , che è attivo , logico, ed onnipotente.
Il problema con questo approccio è che i programmi sono scritti da programmatori , che sono esseri umani e possono mantenere in testa solo un certo numero di dettagli per volta. Man mano che un progetto diventa più grande , il suo cuore procedurale cresce fino al punto in cui è difficile ricordare come funziona il tutto. Minime mancanze di ragionamento ed errori di digitazione causano errori praticamente introvabili. Interazioni complesse e non desiderate cominciano ad emergere man mano che il nucleo procedurale cresce, ed il mantenimento diventa simile al portare in giro una seppia arrabbiata senza che i tentacoli ti tocchino la faccia. Ci sono delle linee guida per la programmazione che aiutano a minimizzare ed a localizzare i bug all'interno del paradigma tradizionale , ma c'è una soluzione migliore che richiede cambiamenti sostanziali al modo in cui lavoriamo .
Quello che la programmazione orientata agli oggetti fa è la delegazione degli aspetti logici più ripetitivi ai dati stessi; esso cambia il nostro concetto di dati da passivo ad attivo. Detta in un altro modo,
-
Smettiamo di trattare ogni blocco di dati come una scatola con un lato aperto che ci permette di metterci le mano dentro e tirare in giro la roba .
- Cominciamo a trattare ogni blocco di dati come una macchina attiva e ben chiusa con un certo numero di bottoni e quadranti ben etichettati .
Ciò che viene descritta qui sopra come una "macchina" può essere internamente molto complessa o molto semplice; non possiamo dirlo dall'esterno, e non permettiamo nemmeno a noi stessi di aprire la macchina (eccetto quando siamo assolutamente certi che ci sia qualcosa che non va nel suo progetto), dunque ci è richiesto di fare cose come attivare gli interruttori e leggere i quadranti per interagire con i dati. Una volta che la macchina è stata costruita , non vogliamo pensare di nuovo a come funziona.
Potreste pensare che ci stiamo causando solo un maggior lavoro, ma questo approccio tende ad essere un buon lavoro nel prevenire che tutto quanto vada male.
Cominciamo con un esempio che è troppo semplice per
essere di valore partico, ma che dovrebbe illustrare almeno
in parte il concetto. La vostra macchina un un
contachilometri. Il suo lavoro è tenere traccia della
distanza che la macchina ha percorso dall'ultima volta
che il suo bottone reset è stato premuto. Come
modellereste una cosa del genere in un linguaggio di
programmazione? In C, il contachilometri sarebbe soltanto una
variabile numerica , magari di tipo float. Il
programma manipolerebbe quella variabile aumentando il suo
valore di poco alla volta , con occasionali reset a zero
quando appropriato. Cosa c'è di sbagliato in
ciò ? Un bug nel programa potrebbe assegnare un valore
errato ala variabile, per una qualsiasi ragione imprevista.
Choiunque abbia programmato in C sa cosa significa spendere
ore o giorni cercando di individuare un bug la cui causa
sembrerà assurdamente semplice una volta individuato.
(il momento dell'individuazione del bug è
comunemente indicato come il rumore di un forte schiaffo
sulla fronte.)
lo stesso problema verrebbe attaccato da un angolo molto differente nel caso di contesto orientato agli oggetti. La prima cosa che un programmatore si chiederebbe nel progettare il contachilometri non sarebbe "quale dei soliti tipi di dato assomiglia abbastanza a questa cosa?" ma "come deve funzionare esattamente questa cosa?". La finisce per essere rilevante. È necessario spendere un po' di tempo per decidere cosa debba fare un contachilometri e come ci si aspetat che il mondo ci interagisca . Decidiamo di costruire una macchina con controlli che ci permettano di incrementare, effettuare il reset it, leggere il valore e basta.
Non forniamo un modo tramite il quale asegnare al contachilometri valori arbitrari; perchè? Perchè sappiamo tutti che i contachilometri non funzionano così. Ci sono solo poche cose che dovreste poter fare con un contachilometri, e quelle sono le sole cose che permettiamo. Inoltre, se qualcosa nel programma incidentalmente cercasse di mettere qualche altro valore (ad esempio, la temperatura impostat nel climatizatore del veicolo) nel contachilometri, ci sarebbe un'indicazione immediata di cosa fosse andato male. Ci verrebe fatto notare all'esecuzione del programma (o magari al momento della compilazione, dipendentemente dalla natura del linguaggio ) che non ci è permesso assegnare valori arbitrari agli oggetti Contachilometri. Il messaggio potrebbe non essere proprio così chiaro , ma sarà ragionevolmente simile. Ciò mnon previene l'errore, vero? Ma ci indirizza subito verso la cuasa. Questo è solo uno dei modi in cui la programmazione OO può risparmiarci un sacco di tempo sprecato.
Spesso effettuiamo un ulteriore passo di astrazione oltre questo,perchè realizzare una fabbrica che faccia macchine risulta semplice come costruire la macchina stessa. Non è probabile che costruiremo un contachilometri singolo direttamente; piuttosto, facciamo si che un numero qualunque di contachilometri possa essere fabbricato comn un singolo processo. Il processo(o se preferite, la fabbrica di cointachilometri) corrisponde a quella che chiamiamo una classe, ed un contachilometri individuale generato dal processo (o dalla fabbrica) corrisponde ad un oggetto. La maggior parte dei linguaggi OO richiede che sia definita una classe prima di avere un nuovo tipo di oggetto, ma ruby non ne ha bisogno.
È importante notare che l'uso di un linguaggio ad oggetti non forza un designo OO corretto. Infatti è possibile scrivere codice in ogni linguaggio che sia poco chiaro, confuso, mal pensato, pieno di bug, pieno di lacune. Ciò che viene fatto da ruby per voi (a differenza, ad esempio, del C++) è far si che la pratica della programmazione ad oggetti risulti abbastanza naturale da far si che anche quando state lavorando su piccola scala non abbiate la necessità di rrivolgervi a codice orribile per risparmiare fatica. Discuteremo dei metodi in cui ruby raggiunge questo ammirevole scopo man mano che questa guid ava avanti; il prossimo argomento sarà "bottoni e quadranti" (metodi degli oggetti) e poi passeremo alle “fabbriche” (classi). Ci siete ancora?