Anch'io ho studiato il paradigma OO da dispense universitarie, quindi non saprei consigliarti un libro in particolare, ma ti direi di leggere articoli online (guarda
quest'altro mio post). Di seguito cerco di farti un'introduzione spicciola all'argomento.
In informatica e anche nella progettazione software un concetto chiave è quello di astrazione: dal latino
abstrahere, significa tralasciare dettagli particolari e guardare un problema da una prospettiva più generale, o più consona a quello che si deve fare in un dato momento. I computer capiscono solo il linguaggio macchina fatto di sequenze di 0 e 1. Negli anni '50 furono inventati i primi linguaggi di programmazione assemblativi, che consentivano di scrivere programmi in maniera più comprensibile per un umano: questo è un primo embrionale esempio di astrazione.
Facendo qualche salto in avanti nel tempo, il costrutto di funzione o procedura, che ben conoscerai se già programmi in C++, altro non è che un'applicazione dell'astrazione funzionale, che consiste nel racchiudere del codice atto a risolvere un certo problema in un blocco invocabile dall'esterno: in questo modo l'utente (programmatore) di una funzione non deve sapere
come funziona ma solo
cosa fa e quali sono le sue regole di invocazione (i parametri ecc.): ecco dove si cela l'astrazione.
Il passo successivo è stata la definizione del concetto di astrazione dati: non si astrae più solo sulle operazioni, ma anche sulla rappresentazione dei dati, il che apre nuove possibilità: si può
incapsulare in un modulo software la rappresentazione dei dati (la loro struttura interna) con delle operazioni lecite che sono le sole autorizzate ad operare su di essi. In questo modo è possibile nascondere la rappresentazione dei dati (
information hiding) in modo che sia sconosciuta all'esterno: il chiamante di un tale modulo non deve più sapere come è fatto (indovinato... astrazione!) ed è quindi indipendente dalla struttura interna dei dati (meno cose si sanno, meno si dipende da esse -> se la rappresentazione dei dati dovesse cambiare, non occorre modificare il codice dell'utilizzatore di quei dati, che ne ignora la struttura). In programmazione, ciò si traduce nei tipi di dato astratti, e nei concetti di oggetto e classe. Ora cercherò brevemente di spiegarteli.
Un tipo di dato astratto è un modulo software in cui dichiari un nuovo tipo, che si basa su tipi già esistenti o primitivi: tipicamente si riferisce ai record, o alle struct di C e C++. Perciò la forma più antica di astrazione dati in programmazione consiste nello scrivere un modulo (tipo un file header di C) in cui dichiari un record (nuovo tipo) e definisci delle operazioni (funzioni o procedure) operanti su di esso. Dichiarando privato il record e facendo sì che vi si possa accedere solo indirettamente attraverso le operazioni lecite (non mi riferisco a un linguaggio in particolare, sono concetti teorici) si realizza l'information hiding e quindi il requisito di protezione dei dati. Puoi immaginare questo modulo come una capsula formata dalla definizione di un tipo, e dalle funzioni operanti su di esso. Alcune di queste operazioni possono servire per operazioni di inizializzazione dei valori del nuovo tipo, e prendono il nome di
costruttori. Un modulo del genere non contiene dati di per sé, ma solo la definizione di un nuovo tipo (struct), perciò per usarlo si deve dichiarare una variabile del nuovo tipo, e solo dopo utilizzarla con le operazioni lecite, alle quali si passerà la variabile come parametro. Quindi questo genere di moduli non conserva uno stato, ovvero non contiene in sé le variabili, ma solo la definizione di tipo.
Un modulo dotato di stato locale, invece, contiene non solo la definizione di un tipo (struct) ma anche una sua
istanza (variabile): un entità del genere prende il nome di oggetto. Perciò un oggetto è un modulo che racchiude la definizione di un tipo, una sua variabile, il cui tempo di vita è pari a quello dell'intero modulo, e dei metodi operanti su di essa (negli oggetti le funzioni/procedure sono dette metodi). Quindi un oggetto = una variabile. Storicamente, il concetto di oggetto è alla base del paradigma Object Based (es. il linguaggio Modula-2).
Il passo successivo è stato quello di astrarre ancora, definendo moduli che rappresentassero non solo una variabile ma una
classe di variabili dello stesso tipo, cioè una classe di oggetti. Una classe è quindi un modulo generico che non identifica un solo oggetto ma un insieme di oggetti dello stesso tipo (la classe sta all'oggetto come la specie sta all'individuo in una popolazione). Questa è una forma di astrazione generica, ovvero si astrae su una dichiarazione di qualcosa, in questo caso sulla dichiarazione di un modulo. Si ebbero così i linguaggi Class Based (es. Ada-83).
Queste nozioni, unite a quella di ereditarietà (
inheritance) costituiscono la base del paradigma Object Oriented (= objects + classes + inheritance secondo una celebre definizione accademica). L'ereditarietà consiste nel poter definire nuove classi (sottoclassi) derivate a partire da altre (superclassi), allo scopo di riutilizzarne il codice o di modellare meglio le entità del problema che si sta affrontando. Altro concetto cardine del paradigma OO è il polimorfismo, di cui ci sono vari tipi e sul quale ci sarebbe molto da dire (vedi
qui). Ti dico solo che il tipo più potente di polimorfismo è quello di inclusione, che dice che un oggetto di una classe B può comparire ogniqualvolta ci si aspetta un oggetto di classe A, se B è sottoclasse di A. Questo in virtù del fatto che la relazione di ereditarietà (in genere) implica una relazione
is a, ovvero un oggetto di classe B è anche un oggetto di classe A, se B eredita (è sottoclasse di) da A. In letteratura questo è indicato come
principio di sostituibilità di Liskov, in nome della prof.ssa Barbara Liskov che lo enunciò negli anni '90.
Per progettare software con il paradigma OO, devi pensare in termini di entità, che sono gli attori nell'universo del programma. Queste entità, che possono essere basate su entità reali prese dal dominio del problema, diventeranno le classi nel programma. Il programma perciò sarà un insieme di classi che interagiscono fra loro (o meglio, i cui oggetti o le cui istanze interagiscono fra loro) scambiandosi messaggi (invocando metodi).
Nel paradigma OO spesso ci si avvale di un linguaggio di modellazione chiamato UML, che serve a rappresentare, mediante diagrammi, sia il modello di dominio del problema che la struttura del software (diagramma delle classi) o il suo funzionamento in esecuzione (diagramma di sequenza), e tante altre cose.
Infine, ti dico che a proposito del paradigma OO (ma non solo) ci sono una serie di principi di progettazione e linee guida, come ad esempio i principi SOLID (di cui ho postato un link nel post menzionato all'inizio), o i principi di alta coesione, basso accoppiamento e il principio DRY (Do not Repeat Yourself). Questi argomenti sono trattati dall'ingegneria del software, materia affascinante e complessa che si occupa proprio della progettazione dei programmi nel loro insieme astraendo (ancora una volta) dai singoli algoritmi. Scusa il pippone ma spero di averti dato un'infarinatura sull'Object Oriented in modo che tu possa per lo meno avere un minimo di basi teoriche. Buona programmazione!