Programmazione Orientata agli Oggetti (OOP): differenze tra le versioni
(17 versioni intermedie di uno stesso utente non sono mostrate ) | |||
Riga 1: | Riga 1: | ||
− | + | '''Vitoantonio Bevilacqua''' [mailto:vitoantonio.bevilacqua@poliba.it vitoantonio.bevilacqua@poliba.it] | |
− | + | ||
+ | '''Antonio Brunetti''' [mailto:antonio.brunetti@poliba.it antonio.brunetti@poliba.it] | ||
+ | |||
+ | '''Gianpaolo Francesco Trotta''' [mailto:gianpaolofrancesco.trotta@poliba.it gianpaolofrancesco.trotta@poliba.it] | ||
== Introduzione == | == Introduzione == | ||
+ | La programmazione orientata agli oggetti (OOP) consiste in un paradigma di programmazione basato su modelli chiamati classi. Gli oggetti sono istanze di classi, e le classi sono messe in relazione fra loro attraverso i principi della ereditarietà (relazione "is a"), della aggregazione e della composizione (relazioni "has a"). | ||
+ | |||
+ | La programmazione orientata agli oggetti supporta tre meccanismi (incapsulamento, ereditarietà e polimorfismo): | ||
+ | # Incapsulamento – si basa sul principio dell’information hiding (attributi o variabili e metodi o funzioni) e permette di nascondere all’utente i dettagli implementativi di una classe; quindi l’utente interagisce con gli oggetti di una determinata classe attraverso le interfacce messe a disposizione dall’oggetto stesso. | ||
+ | # Ereditarietà – la possibilità per una classe derivata di acquisire le caratteristiche di un’altra classe base; tramite le relazioni di generalizzazione/specializzazione, una superclasse definisce un concetto generale, che viene poi specializzato dalle sottoclassi. | ||
+ | # Polimorfismo – la possibilità di utilizzare uno stesso nome per definire metodi diversi permettendo di avere accesso a diverse implementazioni di una funzione attraverso un solo nome (un’interfaccia, più metodi). Il Polimorfismo può essere a compile time e a run time: il polimorfismo a compile time è noto come overloading, quello a run time come overridding. | ||
== Le Classi == | == Le Classi == | ||
+ | Alla base della programmazione ad oggetti c'è il concetto di "classe". Una classe è un costrutto utilizzato per la modellazione di entità, quindi per la creazione (istanziazione) di oggetti; quindi, un'istanza della classe si definisce “oggetto”. | ||
+ | All'interno di una classe si possono distinguere i seguenti elementi: | ||
+ | * Attributo/i; | ||
+ | * Metodo/i. | ||
+ | Gli "attributi", o variabili membro, definiti all'interno di una classe, costituiscono lo stato della classe stessa. Un "metodo" di una classe, invece, definisce una funzionalità della classe; i metodi servono a specificare il comportamento dell’entità (ovvero l’oggetto) di cui la classe è rappresentazione. Trattandosi di funzioni, un metodo può ritornare valori, oppure nulla (come le procedure o “funzioni void”). | ||
+ | |||
+ | L'esempio seguente mostra come è possibile implementare la classe Persona. In questo contesto, essa è caratterizzata da due attributi di tipo stringa (nome e cognome) e una variabile di tipo intero (id). | ||
+ | Per quanto riguarda i metodi, la classe Persona è definita da 2 funzioni costruttore (una senza parametri, l'altra parametrizzata), una funzione per l'inserimento della variabile id, e una funzione per la stampa di nome e cognome. | ||
+ | |||
+ | <syntaxhighlight lang="java" line> | ||
+ | public class Persona | ||
+ | { | ||
+ | private String nome; | ||
+ | private String cognome; | ||
+ | private int id; | ||
+ | |||
+ | public Persona() | ||
+ | { | ||
+ | // Costruttore non parametrizzato della classe persona | ||
+ | } | ||
+ | |||
+ | public Persona(String NOME, String COGNOME) | ||
+ | { | ||
+ | nome = NOME; | ||
+ | cognome = COGNOME; | ||
+ | } | ||
+ | |||
+ | public void stampaNomeCognome() | ||
+ | { | ||
+ | System.out.println(nome + " " + cognome); | ||
+ | } | ||
+ | |||
+ | public void inserisciId(int ID) | ||
+ | { | ||
+ | id = ID; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | In qualunque punto del codice, è possibile istanziare oggetti di classi definite dall'utente. Per esempio, nel metodo main della classe Principale definita di seguito, è istanziato un oggetto di classe Persona (utilizzando il costruttore parametrizzato), e vengono richiamati i metodi definiti precedentemente. | ||
+ | |||
+ | <syntaxhighlight lang="java" line> | ||
+ | public class Principale | ||
+ | { | ||
+ | public static void main(String[] args) | ||
+ | { | ||
+ | Persona p = new Persona("Nome", "Cognome"); | ||
+ | p.inserisciId(1); | ||
+ | p.stampaNomeCognome(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
== Ereditarietà == | == Ereditarietà == | ||
+ | L'ereditarietà è il meccanismo per definire una nuova classe (classe derivata) come specializzazione di un’altra (classe base). Se la classe base modella un concetto generico, la classe derivata modella un concetto più specifico. Attraverso il principio dell'ereditarietà, la classe derivata: | ||
+ | * dispone di tutte le funzionalità (attributi e metodi) di quella base; | ||
+ | * può aggiungere funzionalità proprie; | ||
+ | * può ridefinirne il funzionamento di metodi esistenti (polimorfismo). | ||
+ | |||
+ | In Java, si definisce una classe derivata attraverso la parola chiave “extends”, seguita dal nome della classe base. | ||
== Polimorfismo == | == Polimorfismo == | ||
+ | Nell’ambito dei linguaggi di programmazione, il polimorfismo si riferisce in generale alla possibilità data ad una determinata espressione di assumere valori diversi in relazione ai tipi di dato a cui viene applicata. Il polimorfismo può essere implementato in modi diversi; i principali sono: | ||
+ | * Overloading delle funzioni (method overloading): permette di "ridefinire" un medesimo metodo per set di parametri diversi (stesso nome, ma dominio diverso). | ||
+ | * Ridefinizioni dei metodi ereditati (method overriding): in una sottoclasse è possibile modificare la definizione di un metodo presente nella superclasse facendo in modo tale che lo stesso metodo si comporti diversamente a seconda del tipo di oggetto su cui è invocato. | ||
+ | |||
+ | == Esempio == | ||
+ | Il codice seguente è quello relativo alla classe Persona. Come si può vedere, tale classe contiene un'unica variabile membro, una funzione costruttore (e la sua versione overloadata), e la due ulteriori funzioni. | ||
+ | |||
+ | <syntaxhighlight lang="java" line> | ||
+ | public class Persona { | ||
+ | public int idp; | ||
+ | |||
+ | public Persona() { | ||
+ | System.out.println("Costruttore senza parametri della classe Persona"); | ||
+ | } | ||
+ | |||
+ | public Persona(int IDP) { | ||
+ | idp = IDP; | ||
+ | System.out.println("Costruttore con parametri della classe Persona"); | ||
+ | } | ||
+ | |||
+ | public void stampaIDP() { | ||
+ | System.out.println("Il valore della variabile ID di persona vale " + idp); | ||
+ | } | ||
+ | |||
+ | public void stampaIDP(int notused) { | ||
+ | System.out.println("Il valore della variabile ID di persona vale " + notused); | ||
+ | } | ||
+ | |||
+ | // Funzione che calcola la dimensione in Byte di una variabile intera | ||
+ | public int stampaDimensioneIntero() { | ||
+ | int size = (Integer.SIZE) / Byte.SIZE; | ||
+ | |||
+ | return size; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | Nelle seguenti linee di codice viene definita la classe Studente, derivata dalla classe persona, attraverso la parola chiave '''extends'''. | ||
+ | |||
+ | <syntaxhighlight lang="java" line> | ||
+ | public class Studente extends Persona { | ||
+ | public int ids; | ||
+ | |||
+ | public Studente() { | ||
+ | System.out.println("Costruttore senza parametri della classe Studente"); | ||
+ | } | ||
+ | |||
+ | public Studente(int IDS) { | ||
+ | // Richiamo il costruttore parametrizzato della classe base | ||
+ | super(IDS+1); | ||
+ | |||
+ | // Inizializzo la variabile membro della classe Studente | ||
+ | ids = IDS; | ||
+ | System.out.println("Costruttore con parametri della classe Studente"); | ||
+ | } | ||
+ | |||
+ | public void stampaIDs() | ||
+ | { | ||
+ | System.out.println("Valore dello studente stampato a " + ids); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Infine, nella classe principale vengono istanziati oggetti delle classi precedentemente definite, e richiamate le funzioni dichiarate e definite. | ||
+ | <syntaxhighlight lang="java" line> | ||
+ | |||
+ | public class Principale { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | |||
+ | Persona p = new Persona(3); | ||
+ | p.stampaIDP(); | ||
+ | |||
+ | int dim = p.stampaDimensioneIntero(); | ||
+ | System.out.println("La dimensione dell'intero vale: " + dim); | ||
+ | |||
+ | Studente s = new Studente(5); | ||
+ | s.stampaIDP(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> |
Versione attuale delle 15:20, 3 dic 2019
Vitoantonio Bevilacqua vitoantonio.bevilacqua@poliba.it
Antonio Brunetti antonio.brunetti@poliba.it
Gianpaolo Francesco Trotta gianpaolofrancesco.trotta@poliba.it
Introduzione
La programmazione orientata agli oggetti (OOP) consiste in un paradigma di programmazione basato su modelli chiamati classi. Gli oggetti sono istanze di classi, e le classi sono messe in relazione fra loro attraverso i principi della ereditarietà (relazione "is a"), della aggregazione e della composizione (relazioni "has a").
La programmazione orientata agli oggetti supporta tre meccanismi (incapsulamento, ereditarietà e polimorfismo):
- Incapsulamento – si basa sul principio dell’information hiding (attributi o variabili e metodi o funzioni) e permette di nascondere all’utente i dettagli implementativi di una classe; quindi l’utente interagisce con gli oggetti di una determinata classe attraverso le interfacce messe a disposizione dall’oggetto stesso.
- Ereditarietà – la possibilità per una classe derivata di acquisire le caratteristiche di un’altra classe base; tramite le relazioni di generalizzazione/specializzazione, una superclasse definisce un concetto generale, che viene poi specializzato dalle sottoclassi.
- Polimorfismo – la possibilità di utilizzare uno stesso nome per definire metodi diversi permettendo di avere accesso a diverse implementazioni di una funzione attraverso un solo nome (un’interfaccia, più metodi). Il Polimorfismo può essere a compile time e a run time: il polimorfismo a compile time è noto come overloading, quello a run time come overridding.
Le Classi
Alla base della programmazione ad oggetti c'è il concetto di "classe". Una classe è un costrutto utilizzato per la modellazione di entità, quindi per la creazione (istanziazione) di oggetti; quindi, un'istanza della classe si definisce “oggetto”. All'interno di una classe si possono distinguere i seguenti elementi:
- Attributo/i;
- Metodo/i.
Gli "attributi", o variabili membro, definiti all'interno di una classe, costituiscono lo stato della classe stessa. Un "metodo" di una classe, invece, definisce una funzionalità della classe; i metodi servono a specificare il comportamento dell’entità (ovvero l’oggetto) di cui la classe è rappresentazione. Trattandosi di funzioni, un metodo può ritornare valori, oppure nulla (come le procedure o “funzioni void”).
L'esempio seguente mostra come è possibile implementare la classe Persona. In questo contesto, essa è caratterizzata da due attributi di tipo stringa (nome e cognome) e una variabile di tipo intero (id). Per quanto riguarda i metodi, la classe Persona è definita da 2 funzioni costruttore (una senza parametri, l'altra parametrizzata), una funzione per l'inserimento della variabile id, e una funzione per la stampa di nome e cognome.
public class Persona
{
private String nome;
private String cognome;
private int id;
public Persona()
{
// Costruttore non parametrizzato della classe persona
}
public Persona(String NOME, String COGNOME)
{
nome = NOME;
cognome = COGNOME;
}
public void stampaNomeCognome()
{
System.out.println(nome + " " + cognome);
}
public void inserisciId(int ID)
{
id = ID;
}
}
In qualunque punto del codice, è possibile istanziare oggetti di classi definite dall'utente. Per esempio, nel metodo main della classe Principale definita di seguito, è istanziato un oggetto di classe Persona (utilizzando il costruttore parametrizzato), e vengono richiamati i metodi definiti precedentemente.
public class Principale
{
public static void main(String[] args)
{
Persona p = new Persona("Nome", "Cognome");
p.inserisciId(1);
p.stampaNomeCognome();
}
}
Ereditarietà
L'ereditarietà è il meccanismo per definire una nuova classe (classe derivata) come specializzazione di un’altra (classe base). Se la classe base modella un concetto generico, la classe derivata modella un concetto più specifico. Attraverso il principio dell'ereditarietà, la classe derivata:
- dispone di tutte le funzionalità (attributi e metodi) di quella base;
- può aggiungere funzionalità proprie;
- può ridefinirne il funzionamento di metodi esistenti (polimorfismo).
In Java, si definisce una classe derivata attraverso la parola chiave “extends”, seguita dal nome della classe base.
Polimorfismo
Nell’ambito dei linguaggi di programmazione, il polimorfismo si riferisce in generale alla possibilità data ad una determinata espressione di assumere valori diversi in relazione ai tipi di dato a cui viene applicata. Il polimorfismo può essere implementato in modi diversi; i principali sono:
- Overloading delle funzioni (method overloading): permette di "ridefinire" un medesimo metodo per set di parametri diversi (stesso nome, ma dominio diverso).
- Ridefinizioni dei metodi ereditati (method overriding): in una sottoclasse è possibile modificare la definizione di un metodo presente nella superclasse facendo in modo tale che lo stesso metodo si comporti diversamente a seconda del tipo di oggetto su cui è invocato.
Esempio
Il codice seguente è quello relativo alla classe Persona. Come si può vedere, tale classe contiene un'unica variabile membro, una funzione costruttore (e la sua versione overloadata), e la due ulteriori funzioni.
public class Persona {
public int idp;
public Persona() {
System.out.println("Costruttore senza parametri della classe Persona");
}
public Persona(int IDP) {
idp = IDP;
System.out.println("Costruttore con parametri della classe Persona");
}
public void stampaIDP() {
System.out.println("Il valore della variabile ID di persona vale " + idp);
}
public void stampaIDP(int notused) {
System.out.println("Il valore della variabile ID di persona vale " + notused);
}
// Funzione che calcola la dimensione in Byte di una variabile intera
public int stampaDimensioneIntero() {
int size = (Integer.SIZE) / Byte.SIZE;
return size;
}
}
Nelle seguenti linee di codice viene definita la classe Studente, derivata dalla classe persona, attraverso la parola chiave extends.
public class Studente extends Persona {
public int ids;
public Studente() {
System.out.println("Costruttore senza parametri della classe Studente");
}
public Studente(int IDS) {
// Richiamo il costruttore parametrizzato della classe base
super(IDS+1);
// Inizializzo la variabile membro della classe Studente
ids = IDS;
System.out.println("Costruttore con parametri della classe Studente");
}
public void stampaIDs()
{
System.out.println("Valore dello studente stampato a " + ids);
}
}
Infine, nella classe principale vengono istanziati oggetti delle classi precedentemente definite, e richiamate le funzioni dichiarate e definite.
public class Principale {
public static void main(String[] args) {
Persona p = new Persona(3);
p.stampaIDP();
int dim = p.stampaDimensioneIntero();
System.out.println("La dimensione dell'intero vale: " + dim);
Studente s = new Studente(5);
s.stampaIDP();
}
}