Lezione 1 - Perche' la programmazione ad oggetti?


Se dovete stampare questo documento usate questo link
L'attivita' del programmatore
Possiamo considerare l'attivita' del programmatore come quella di un ingegnere che costruisce un ponte.La cosa piu' importante e' che il ponte non crolli,ovvero che il programma funzioni. La programmazione a oggetti permette di costruire ponti che non crollano? Ahime no! Qualsiasi tecnica di costruzione programmi usiamo niente ci garantisce il successo del programma. Questo e' dovuto solo all'abilita' del programmatore. Allora a cosa serve la programmazione ad oggetti?Bene, se siamo riusciti a costruire un programma funzionante in qualche modo, come seconda cosa importante , vorremmo che il programma sia di facile manutenzione. Infine vorremmo poterlo usare per costruire "altri ponti che non cadono". Ora la vecchia tecnica funzionale (programma=struttura di dati+funzioni) permette di costruire programmi "monolitici" di difficile manutenzione e riuso. La programmazione a oggetti invece spezzando il programma in tanti oggetti indipendenti dovrebbe facilitare queste operazioni. L'unica struttura di dati e libreria di funzioni del programma funzionale si e' trasformata in una serie di oggetti ognuno dei quali ha una propria struttura di dati e una libreria di funzioni.Come si vede un programma ad oggetti e' un'ulteriore astrazione del programma funzionale che potrebbe essere considerato come un programma a un solo oggetto. Per tornare alla nostra metafora ingegnieristica, ora stiamo costruendo ponti con moduli prefabbricati in modo da rendere piu' facile la costruzione di altri ponti simili.

I linguaggi ad oggetti:un'ulteriore astrazione Ora i dati sono racchiusi assieme alle funzioni negli oggetti:questa e' l'ulteriore astrazione introdotta dai linguaggi a oggetti.E,come vedremo, i dati sono accessibili solo attraverso speciali funzioni.
La struttura di dati che descrive lo stato di un oggetto viene definita ora dalle proprieta' dell'oggetto e realizzata con le variabili dell'oggetto. (Queste sono chiamate talvolta anche campi :fields in inglese). Mentre le funzioni che descrivono il comportamento dell'oggetto diventano i metodi dello stesso. La classe e' un pezzo di codice che descrive le proprieta' e i metodi di un oggetto. Per cui ora un programma e' formato da una serie di classi. Si dice anche che metodi e variabili sono membri della classe(in C++ infatti variabili e metodi si chiamano data members e member functions).
I linguaggi ad oggetto hanno particolari costrutti che permettono di trattare gli stessi oggetti.In particolare l'operatore new permette di creare un oggetto descritto in una classe o come si dice di instanziare lo stesso o crearne una istanza.Questo corrisponde in pratica alla chiamata di un particolare metodo il costruttore che ha lo stesso nome della classe e che provvede a inizializzare la struttura di dati dell'oggetto(in effetti secondo i puristi il costruttore non e' un metodo ). L'accesso a una particolare proprieta' o metodo di un oggetto avviene attraverso l'operatore ".":nomeoggetto.nomeproprieta e nomeoggetto.nomemetodo().I due riferimenti si distinguono solo per la presenza delle parentesi tonde per i metodi.

Un caso concreto
Volendo costruire un programma che tratti eventi della fisica delle alte energie con le loro tracce e i loro run, nel caso della programmazione funzionale costruiremmo una struttura di dati contenente Evento Traccia e Run piu' una libreria di funzioni che fanno diverse operazioni su questi dati. (Da notare che il nome funzione qui indica la stessa cosa di procedura, routine,sottoprogramma).
Nella programmazione a oggetti partiremmo subito dai 3 oggetti Traccia,Evento e Run e per ogni oggetto definiremmo quindi le proprieta' e il comportamento.Le funzioni dell'approccio procedurale appaiono ora come metodi all'interno delle classi. Un programma utente ora invece di richiamare una serie di funzioni per manipolare i dati di una struttura, instanzia gli oggetti che servono e quindi chiede agli stessi oggetti di fare certe cose invocando i loro metodi.
Il modo di affrontare il problema e' cambiato completamente: mentre prima ragionavamo mettendoci nei panni del computer e definendo delle funzioni che dovevano portare alla soluzione, ora ragionamo usando i termini del problema e definiamo degli oggetti che modellano il problema e ognuno dei quali ha particolari capacita' che permettono di risolvere il problema. Quindi non e' piu' il computer che disegna l'evento ma e' l'evento che disegna se stesso come se ogni oggetto fosse un piccolo computer specializzato.
E' interessante confrontare la documentazione di un progetto software vecchio stile e uno ad oggetti.La relativamente semplice descrizione della struttura dei dati dell'esperimento (una lista di "banche" tutte uguali in formato ma di diverso contenuto) e' diventata un'enorme lista di centinaia di classi ognuna con la propria interfaccia particolare: il risultato e' un aumento di complessita' enorme per il povero programmatore!

Incapsulazione,Ereditarieta',Polimorfismo
Secondo i puristi,quanto detto finora , non basta per fare un linguaggio ad oggetti:sono necessarie inoltre le tre caratteristiche che adesso vedremo.
Chi usa un oggetto deve conoscere solo i nomi delle proprieta' e dei metodi(ovvero la sua interfaccia) ma non ha bisogno di conoscere i dettagli dell'implementazione dello stesso che rimangono incapsulati,nascosti nello stesso:e' questa la cosiddetta incapsulazione dei linguaggi OO.Questa incapsulazione dovrebbe isolare il codice di un oggetto evitando che il suo cambiamento possa influenzare il codice di un altro oggetto e si realizza attraverso particolari attributi che proteggono l'accesso a classi,metodi e variabili che vogliamo rendere inaccessibili.In particolare incapsulazione significa proteggere le proprieta' rendendole accessibili solo attraverso speciali metodi di accesso (getValue) e di settaggio (setValue).
Se vogliamo modificare una classe non abbiamo bisogno di modificare la classe originaria(anche se potremmo farlo) come succedeva nella programmazione funzionale, ma i linguaggi a oggetti ci permettono di definire una nuova classe che estende o eredita l'altra classe aggiungendo nuovi metodi e proprieta' ed eventualmente ridefinendo (implementando)quelli della classe madre: e' questa l'ereditarieta' dei linguaggi OO.
Sfruttando questo meccanismo i programmi sono in effetti delle gerarchie di classi che condividono proprieta' e metodi:quando richiamiamo un metodo di un oggetto proveniente da una classe in questa gerarchia non stiamo richiamando un metodo ben definito, ma e' compito del linguaggio scoprire all'atto dell'esecuzione quale metodo andra' richiamato.Questo e' il polimorfismo dei linguaggi OO.Per questo non abbiamo per i linguaggi ad oggetto la fase di link, ma l'aggancio a nuove classi avviene in maniera dinamica all'atto dell'esecuzione.Questo particolare modo di lavorare permette di scrivere programmi che hanno messaggi "generici" come oggetto.disegna() che funzionano con diversi tipi di oggetti ed anche con oggetti che saranno definiti solo nel futuro.
Incapsulazione,ereditarieta' e polimorfismo sono le 3 principali proprieta' che definiscono i linguaggi OO.

In concreto
Tornando ora al nostro esempio degli eventi, se ora vogliamo aggiornare il codice, perche' si e' aggiunto un nuovo dato e dobbiamo modificare il sottoprogramma di disegno degli eventi in modo da utilizzare questo dato, nella programmazione ad oggetti questo non richiede alcuna modifica al codice originale. Viene solo aggiunta una nuova classe NuovoEvento aggiungendo le sole proprieta' e metodi modificati. Le applicazioni precedenti funzionano ancora perfettamente perche' il codice originario non e' stato cambiato.Inoltre ,se il nuovo codice ha solo ridefinito vecchi metodi, i vecchi programmi funzioneranno anche con i nuovi eventi senza cambiare una riga di codice.
Nella programmazione funzionale siamo costretti a modificare il codice precedente con possibili ripercussioni su tutte le applicazioni che gia' lo usavano.Intanto dobbiamo aggiungere un nuovo dato in tutte le procedure. Poi dovremo riscrivere tutte le procedure che usano il nuovo dato.

This,super e altri oggetti particolari
Nei linguaggi a oggetti le parole this e super hanno un significato speciale.This si riferisce al nome dell'oggetto del quale in questo momento stiamo eseguendo qualche metodo,super si riferisce invece alla classe madre di this.Infine in Java tutti gli oggetti sono discendenti da un'unica classe : la classe Object. Una sottoclasse viene creata con l'istruzione extends e puo' ereditare da una sola superclasse. Cioe' in Java non abbiamo l'ereditarieta' multipla. La ridefinizione di un metodo si dice in gergo override mentre l'overload di un metodo si riferisce alla possibilita' di avere piu' metodi con lo stesso nome ma con diverso numero e tipo di argomenti. In Java non possiamo invece sovraccaricare(overload in italiano) gli operatori come +. In Java tutto e' oggetto eccetto gli otto tipi primitivi ; percio' per poter operare su di essi(come oggetti) sono state create 8 classi Wrapper.

Classi Astratte e Interfacce: 2 classi molto speciali La classe progenitrice di tutte le altre e' spesso una abstract class della quale non si possono creare istanze e possiamo usarla solo estendendola.
Un'interfaccia(interface) e' anch'essa una classe astratta che contiene solo metodi non implementati. Tali metodi possono essere implementati da un'altra classe senza bisogno di estendere la classe ma implementando la stessa con l'istruzione implements.Una classe puo' estendere un'unica classe ma implementare un numero qualsiasi di interfacce. E' questa la maniera di Java di realizzare l'ereditarieta' multipla.
Sia le classi astratte che le interfaccie permettono di descrivere delle interfaccie che saranno implementate estendendo le stesse. Ad esempio,in Java, per descrivere come deve essere scritto un oggetto che lavori nel browser si e' definita la classe astratta Applet. Chi vuole scrivere un programma che opera in una finestra di un documento Web estende questa classe implementandone i metodi come paint che indica cosa deve essere disegnato nella finestra.

Packages,strutture generate dall'ereditarieta' e strutture di dati
Nella programmazione ad oggetti e' facile confondersi se non si capisce bene la differenza tra classi e oggetti e tra strutture formate da classi e strutture formate da oggetti. Inoltre abbiamo strutture presenti solo sul programma scritto e strutture che si creano all'atto dell'esecuzione del programma. Gli oggetti,ad esempio,esistono solo all'atto dell'esecuzione a differenza della classe che e' presente sia nel programma scritto che all'atto dell'esecuzione sotto forma di variabili e metodi di classe. Queste variabili e questi metodi vengono detti in Java statici perche' realizzati attraverso la parola chiave static. Per cui all'atto dell'esecuzione, per ogni classe usata, avremo le variabili e i metodi statici sempre presenti piu' un numero variabile(anche 0) di oggetti istanziati. Ogni oggetto istanziato ha le proprie variabili e i vari oggetti attraverso queste variabili possono puntare l'uno all'altro creando una struttura di dati:ad esempio una lista. Viceversa le classi,oltre ad essere organizzate nella loro struttura gerarchica di ereditarieta' (che ha come effetto all'atto dell'esecuzione che tutte le classi madri di una classe usata sono caricate) sono raggruppate anche in packages(pacchetti). La divisione in packages non ha niente a che fare con l'ereditarieta' e viene introdotta per questioni di comodita'. Le classi di uno stesso package hanno un'accesso facilitato alle proprie variabili e e ai propri metodi. Questo e' utile per facilitare la scrittura di classi di servizio che servono solo a realizzare la funzionalita' presente nelle classi pubbliche del package.Tutte le classi di un package dividono la stessa cartella che di solito ha lo stesso nome del package.Queste cartelle possono formare una struttura gerarchica che non ha nessun particolare significato.Invece il singolo file,in Java,contiene una sola classe pubblica e un numero qualsiasi di classi di servizio e puo' quindi essere considerato come un minipackage.
Il livello di accesso di una classe,variabile o metodo e' definito in Java tenendo conto di queste strutture.Questo va infatti da private(accesso consentito nella sola classe) a livello di default (accesso consentito nel pacchetto), a protected accesso consentito alle classi nel pacchetto e alle classi figlie in altri pacchetti; a public accesso generalizzato.

Persistenza degli oggetti
La programmazione ad oggetti,mettendo l'accento sugli oggetti, cambia completamente il modo di considerare l'input/output. Prima si era portati a considerare le strutture di dati in memoria come separate e diverse dalle strutture di dati sulle memorie di massa, con istruzioni di I/O che leggendo da nastro o disco popolano le strutture di dati in memoria.In un linguaggio ad oggetti questo modo di porre il problema e' (o dovrebbe essere) obsoleto: abbiamo solo oggetti persistenti (cioe' con una copia su memoria di massa) che vengono copiati,quando servono, tali e quali e poi eventualmente riscritti se cambiano di stato. Percio' in un linguaggio ad oggetti invece di parlare di I/O si parla di persistenza degli oggetti. Solo alcuni degli oggetti di un programma saranno persistenti, mentre la maggioranza sara' del tipo transiente che viene distrutto quando il programma termina l'esecuzione.Java permette di leggere e scrivere oggetti realizzando la persistenza ma una soluzione definitiva di questo problema si avra' solo con l'affermarsi dei data base ad oggetti in contrapposizione ai data base relazionali ora piu' in auge.

Scomposizione (ottimale) di un programma in oggetti
Trovare quali oggetti implementare per risolvere un certo problema, e' una cosa difficile. Per questo,nel caso di grandi progetti con piu' persone, esistono anche delle tecniche di analisi e disegno orientate agli oggetti. Prima di cominciare a scrivere la prima riga di codice c'e una lunga attivita' di progettazione simile a quella in uso nell'ingegneria per decidere la struttura del programma rappresentandola con appositi diagrammi.Un approccio possibile in questa fase viene indicato col nome di L'UML o Unified Modeling Language.
L'UML non e' un linguaggio di programmazione ma una metodologia che basandosi su una diecina di differenti tipi di diagrammi permette di documentare l'attivita' di sviluppo del software dall'inizio alla fine. Ha il vantaggio di non essere legato a un particolare linguaggio ad oggetti e quindi di trattare classi e oggetti senza entrare nei dettagli dell'implementazione. Una classe viene rappresentata da un rettangolo diviso in 3 da due linee



in modo da poter contenere il nome, gli attributi e le operazioni. Una scritta in corsivo rappresenta una classe o un'operazione astratta.Ovviamente gli attributi sono cio' che prima abbiamo chiamato proprieta' e le operazioni sono i metodi. E' possibile rappresentare anche le relazioni(statiche) tra le classi. Sono previste relazioni di ereditarieta'/specializzazione/generalizzazione (freccia con triangolo vuoto) e relazioni di associazione.La relazione di associazione prevede la possibilita' di rappresentare quattro tipi diversi di associazione:composizione,aggregazione,associazione generica, dipendenza. I quattro tipi si distinguono per essere rappresentati da frecce con rombo pieno, freccia con rombo vuoto, semplice linea di collegamento piena, semplice linea di collegamento tratteggiata.
Con questi simboli si crea il diagramma delle classi . Questo viene preceduto nella progettazione dal diagramma dei casi d'uso che permette di specificare le richieste dell'utente.Il diagramma di sequenza permette di seguire nel tempo lo scambio di messaggi tra oggetti. Seguono altri diagrammi che permettono di documentare ogni aspetto del sistema.Altro esempio di diagrammi UML.
Invece i Design Pattern ,nonostante il nome, vengono usati durante l'implementazione in quanto aiutano a risolvere i problemi piu' comuni che sorgono nel trattare gli oggetti.
I Design Patterns sono stati introdotti per la prima volta in un libro dello stesso nome della cosiddetta gang dei quattro(Gamma,Helm,Johnson e Vlissides) pubblicato nel 1994.Questo libro ha avuto un grande effetto sulla comunita' dei programmatori ed ora parecchi dei 23 pattern presentati nel libro, sono diventati parte del gergo se non delle implementazioni dei nuovi linguaggi incluso Java. Ecco la classificazione originale: Ad esempio Java ha implementato il pattern Iterator con l'oggetto Enumeration (vedere la lezione 3 di questo corso). Il pattern Observer viene usato in diverse parti di Java quando uno o piu' oggetti hanno bisogno di aggiornarsi quando un altro oggetto cambia stato.Ad esempio un oggetto vuole sapere quando il download di un'immagine e' completato o quando l'utente ha fatto qualcosa. Ogni pattern non fa che risolvere qualche problema ben definito in una maniera ottimale ed evita di dover reinventare ogni volta la ruota. Ad esempio, per fare un altro caso, il pattern Singleton risolve il problema di avere un oggetto in unica copia. In definitiva l'idea di base dietro ai design pattern (che nonostante il nome hanno a che fare piu' con l'implementazione che col disegno in quanto sono maniere standard di risolvere problemi ricorrenti) e' quello di creare una nuova astrazione che vada oltre gli oggetti.

Invece al livello dei problemi eseguibili da parte di una sola persona potete usare questa regola grossolana per decidere se il vostro programma e' stato diviso in un numero ottimale di oggetti:la lunghezza del codice di ogni singola classe non dovrebbe superare le 2/3 pagine. Se una classe diventa troppo grande allora e' arrivato il momento di definire nuovi oggetti.

In definitiva ...
La classe incapsula certi dati e i metodi per accedervi.Mentre prima chiunque poteva modificare quei dati, ora dovete farlo passando attraverso la classe e questo evita un sacco di problemi.
Un programma e' un insieme di oggetti che comunicano tra di loro mandandosi dei messaggi. Questi messaggi, consistenti nella chiamata di un metodo,chiedono all'oggetto che riceve il messaggio di fare qualcosa.
Potete pensare a un oggetto come un tipo di dati piu' complesso che si aggiunge agli 8 tipi primitivi di dati.A questi dati puoi richiedere di fare certe operazioni su se stessi, richiamando dei metodi.
Ogni oggetto ha una sua propria memoria che puo' contenere dati primitivi e altri oggetti. Questo permette di creare degli oggetti di grande complessita' partendo da oggetti semplici e impacchettandoli in un nuovo oggetto.
In un programma a oggetti potete inviare dei messaggi generici, a cui ogni oggetto rispondera' a seconda della sua posizione nella gerarchia delle classi.
L'idea di fondo e' di far scrivere ai migliori programmatori degli oggetti che poi possano essere riusati da tutti per costruire nuove applicazioni.
Java,con le sue librerie specializzate,fornisce migliaia di questi oggetti pronti per l'uso. Questo ,non solo per facilitare il lavoro dei programmatori, ma anche per fornire un ambiente di lavoro identico su tutte le piattaforme su cui Java gira.Imparare a programmare in Java significa innanzitutto conoscere queste librerie di classi.

Java Development Kit : JDK
Per programmare in Java occorre procurarsi il JDK della Sun (Java Development Kit) che e' la distribuzione ufficiale in una delle sue varie versioni. Java viene fornito come una libreria di classi suddivise in packages e compresse nel formato zip piu' una serie di programmi eseguibili. Talvolta questo e' gia' disponibile sul computer che usate.Per scoprirlo e sapere quale versione e' disponibile basta dare il comando(da finestra MSDos su PC):
java -version


Applicazione 1:Il piu' semplice programma Java

class Ciao{
      public static void main (String args[]){
            System.out.println("Ciao a tutti");
     }
  }
Per scrivere programmi Java usate un editore di testi come Wordpad su Pc o vi su Unix.Copiate il programma e salvatelo in un file di nome Ciao.java L'istruzione
javac Ciao.java
compilera' il programma' creando un file: Ciao.class Finalmente l'istruzione
java Ciao
fara' eseguire il programma che scrivera' la frase Ciao a Tutti.



Applet 2:Il piu' semplice applet


Un applet e' un programma Java che gira all'interno di un browser.
import java.awt.*;
import javax.swing.*;
public class Ciao extends JApplet {

 Display disegno;
 
 public void init(){
  disegno = new Display();  
  setContentPane(disegno);
 }
class Display extends JPanel {
 public void paintComponent(Graphics g){
   super.paintComponent(g);
   g.drawString("CiaoATutti",5,25);
  }
 }
 }

Salvatelo su disco in un file Ciao.java come avete fatto con l'altro programma e poi compilatelo col solito comando:
javac Ciao.java
Questo crea il compilato Ciao.class. Per poterne vedere l'esecuzione dovete inserire il programma Java in una pagina Web. Questo si fa scrivendo un documento HTML con l'istruzione:
<APPLET CODE=Ciao.class Width=300 Height=50>
</APPLET>
che va salvato su disco col nome Ciao.html nella stessa cartella del programma Java. Ora basta leggere il documento col browser per poter vedere il programma in esecuzione. (Se fate queste operazioni su una cartella visibile dal Web e volete accedere all'applet con una URL dovete probabilmente sproteggere i file Ciao.html e Ciao.class: su macchine Unix usate il comando
chmod 644 nomefile

Ma in fase di costruzione di un applet e' molto piu' comodo usare uno speciale programma del jdk chiamato appletviewer:questo lo si usa col comando
appletviewer Ciao.html
Cercate di familiarizzarvi con questa procedura che sara' usata in tutto il corso.
INDIETRO a Imparate Java in una settimana
INDIETRO a Seminario su Java
Maintained by : info@zitogiuseppe.com
Ultimo aggiornamento: