Programmazione Java Avanzata e Mobile Introduzione al corso Anno 2013/2014 Programmazione Java Avanzata e Mobile Struttura del corso/slide: - Background Hibernate Struts Spring (opzionale) Android Ogni lezione prevede la presentazione dell’argomento dal punto di vista teorico e l’utilizzo delle conoscenze apprese attraverso un’esercitazione Programmazione Java Avanzata e Mobile Testi Consigliati Dispense delle lezioni Java Persistence with Hibernate, Second Edition of Hibernate in Action, Christian Bauer and Gavin King, Manning Publications Co. Struts 2 in Action, Donald Brown, Chad Michael Davis, and Scott Stanlick, Manning Publications Co. Android Developers http://developer.android.com Si raccomanda la frequenza del corso, in modo particolare delle esercitazioni. Programmazione Java Avanzata e Mobile Modalità dell’esame L’esame consiste in una prova progettuale relativa all’implementazione di una Web Application. Il progetto è tipicamente svolto da gruppi di due persone. La discussione del progetto è effettuata solo durante il periodo di esami. Programmazione Java Avanzata e Mobile Software utilizzati: Eclipse Java EE IDE for Web Developers (cons. Kepler) MySQL e MySQLWorkbench (cons. 5) Hibernate 4 Struts 2.3 Android Developer Tools (cons. v22) Programmazione Java Avanzata e Mobile Richiami di Java (opzionali) Anno 2013/2014 Richiami di base su Java Creazione di riferimenti e instanziazione Object o; (crea un riferimento ad Object ma non lo istanzia) Object o = new Object(); (alloca lo spazio in memoria) 7 Richiami di base su Java I tipi primitivi (numerici) come int, float, double, ecc. non sono “oggetti”. Per utilizzarli come tali esistono le classi “wrapper” Integer, ecc. Non esiste il concetto di puntatore, ma solo quello di riferimento. Tutte le variabili che contengono oggetti sono riferimenti, tranne quelle che contengono tipi primitivi. Non esiste il concetto di “distruttore” (per la presenza nella VM del Garbage Collector). 8 Richiami di base su Java Il passaggio di parametri è fissato: per valore (tipi primitivi) per riferimento (tipi oggetto) Le variabili locali devono essere inizializzate prima di essere utilizzate. Gli attributi, se non inizializzati dal costruttore, assumono il valore zero per i tipi numerici e null per quelli di tipo riferimento a oggetto. 9 Programmazione Object-Oriented Paradigma di programmazione che mette al centro la nozione di “oggetti”, raggruppati in “classi”. Una classe definisce un tipo di dati che rappresenta una nozione (concreta o astratta della realtà). Ogni oggetto è definito da un insieme di dati (attributi) e funzioni (metodi). Gli attributi ne rappresentano lo stato, i metodi ne modellano il comportamento. 10 Programmazione Object-Oriented Lo scopo principale dell’OOP è consentire il riuso del codice attraverso la sua modularizzazione per minimizzare la presenza di bug e accelerare i tempi di sviluppo del software. Caratteristiche fondamentali dei linguaggi OOP: Incapsulamento Ereditarietà Polimorfismo 11 Incapsulamento Ogni oggetto contiene sia i propri dati che le funzioni che operano su tali dati. L’accesso ai dati di un oggetto avviene attraverso metodi di accesso (usando i qualificatori di accesso). Oggetti come “scatole nere”: non sono noti i dettagli di funzionamento, ma solo l'interfaccia. E’ possibile cambiare l’implementazione di un oggetto senza modificare il resto del codice. 12 Incapsulamento in Java Qualificatori di accesso: private package (per default) protected (package + sottoclassi) public L’incapsulamento richiede la visibilità minima 13 Ereditarietà Estensione di una classe “base” La classe derivata “eredita” (li vede come se fossero i suoi) attributi e metodi (non privati) della classe base. La classe derivata può ridefinire attributi/metodi della classe base o avere attributi/metodi aggiuntivi. 14 Ereditarietà in Java Sintassi: class Derivata extends Base Aggiunta/overriding di attributi/metodi Accesso ad attributi/metodi della classe Base: uso di super Ereditarietà “pubblica”: non è possibile ridurre l’accesso ad attributi/metodi nella classe Derivata attraverso overriding. Solamente ereditarietà singola. 15 Ereditarietà in Java Esempio: public class Studente extends Persona Studente può aggiungere attributi (es. matricola) o metodi - es. getMatricola() Studente può ridefinire - es. void print() metodi di Persona 16 Polimorfismo Permette di avere metodi con la stessa signature che si comportano in modo differente a seconda degli oggetti su cui vengono invocati. Polimorfismo ed ereditarietà: uso di metodi che accettano in input il super-tipo ma il cui comportamento si adatta ai sotto-tipi. Late binding – la scelta del metodo corretto avviene a run-time e non a compile-time. 17 Classi Astratte in Java Classi in cui alcuni metodi sono astratti (non sono implementati, c’è solo la signature). Sintassi: public abstract class FiguraGeometrica { public abstract double perimetro(); …} Non è possibile istanziare oggetti di una classe astratta. E’ possibile dichiarare riferimenti di tipo “astratto”. Per istanziare oggetti è necessario derivare una classe che implementa tutti i metodi astratti. 18 Classi Astratte in Java Esempio: public class Quadrato extends FiguraGeometrica { public double perimetro(){ return misuraLato*4; } … } E’ possibile istanziare oggetti di Quadrato solo se tutti i metodi astratti ereditati sono implementati. E’ possibile inizializzare un riferimento FiguraGeometrica con un oggetto Quadrato (subtyping). 19 Interfacce in Java Simili alle classi astratte, ma possono contenere solo signature di metodi (pubblici) senza implementazione. Servono a “forzare” una classe a implementare un’insieme di metodi. I metodi sono pubblici per default. Sintassi: public interface FiguraGeometrica { public double perimetro(); … } Non è possibile istanziare oggetti di una interfaccia. E’ possibile dichiarare riferimenti di tipo “interfaccia”. 20 Interfacce in Java E’ possibile istanziare oggetti di una classe che implementa quell’interfaccia. Sintassi: public class Quadrato implements FiguraGeometrica { public double perimetro(){ return misuraLato*4; } … } 21 Eccezioni controllate e non Eccezioni controllate: Il compilatore controlla che noi non le ignoriamo dovute a circostanze esterne che il programmatore non può evitare Es. FileNotFoundException Eccezioni non controllate: rappresentano un errore del programmatore Es. ArrayIndexOutOfBoundsException Gestione delle Eccezioni Due Possibilità: 1. Catturare l’eccezione blocchi try / catch 2. Dire al compilatore che vogliamo terminare il metodo quando si verifica l’eccezione. Usare throws Enunciato finally Per eseguire task che devono essere comunque completati (sia in presenza che in assenza di errori, es. chiusura file) Programmazione Java Avanzata e Mobile Nozioni aggiuntive di Java Anno 2013/2014 Nozioni aggiuntive di Java Convenzioni sui nomi Programmazione generica Annotations Serializzazione Logging 25 Convenzioni I nomi delle classi sono sostantivi che iniziano in maiuscolo Es. class Studente{ … } I nomi delle istanze iniziano con lettera minuscola Es. Studente studente=new Studente(); Tutte le variabili (locali) devono essere inizializzate prima di essere utilizzate. Es. void contaRipetizioni (){ int cont=0; … 26 Convenzioni Se il nome è composto da più di un sostantivo, ognuno di questi avrà l'iniziale maiuscola Es. class StudenteLavoratore{ ... } I nomi dei metodi sono composti da un verbo, oppure un verbo + sostantivo; solo la prima iniziale è minuscola Es. int getRicaviEsercizio() I nomi delle variabili membro sono composti da sostantivi e usano la stessa convenzione dei metodi Es. int annoIscrizione 27 Convenzioni I metodi di accesso sono relativi agli attributi: Es. int getMatricola() … Es. void setMatricola(int matricola) … Per gli attributi booleani si utilizza la forma ‘is’ Es. boolean isReady() 28 Convenzioni Le costanti sono tutte in maiuscolo, con i sostantivi separati da underscore Es. final int MAX_VOTO = 30 I nomi dei package sono tutti in minuscolo, con i sostantivi separati da underscore Es. package ing.programmazione_java_avanzata L’organizzazione in package è un elemento molto importante 29 Programmazione Generica Tecnica di programmazione per cui è possibile realizzare strutture dati e algoritmi che funzionano con diversi tipi di dati. public class ArrayList<T> { public ArrayList() {...} public void add(E element) {...} ... } Usata in questo modo: ArrayList<String> listaNomi = new ArrayList<String>(); 30 Programmazione Generica Collection rappresenta il concetto di gruppo di oggetti (duplicati o no, ordinati o no, con priorità, …) 31 Programmazione Generica Per cui potrei: Set insieme=new HashSet(); Element e=new Element(); Insieme.add(e); Gli elementi possono essere di diverso tipo. E’ necessario un cast in fase di accesso in lettura. E’ preferibile parametrizzare. Set<Element> insiemeElementi=new HashSet<Element>(); 32 Java Annotations Dalla versione 5.0 Java ha introdotto dei meccanismi per inserire metadati descrittivi nel codice stesso Questi metadati (dati a proposito dei dati) possono essere usati per rendere espliciti alcuni comportamenti Sono immersi nel codice ed esistono analizzatori in grado di adoperarli per semplificare il lavoro si sviluppo o potenziare il Design By Contract Es. @Override, @Deprecated, @SuppressWarnings Molti framework definiscono annotation proprie 33 Serializzazione La Serializzazione, in Java, è la trasformazione di un oggetto nella sua rappresentazione binaria Un oggetto serializzato può essere salvato su disco, su database, inviato via internet etc Gli oggetti, per essere serializzabili, devono implementare l'interfaccia Serializable Ogni oggetto serializzabile deve avere una variabile membro serialversionUID È una sorta di hash che serve a controllare che non vengano deserializzati oggetti di versioni diverse 34 Serializzazione serialversionUID può essere gestita o meno dal programmatore. Tipicamente abbiamo: public class Utente implements Serializable { private static final long serialVersionUID = 1L; … } 35 Logging Nella fase di testing o di debugging può essere utile evidenziare il verificarsi di situazioni particolari: public class Esame { private int voto; … void setVoto(int voto){ if (voto > 30) System.out.println(“Errore …”); this.voto=voto; } … 36 Logging L'output su console (System.out.println) non è una soluzione ideale in quanto: - La console deve essere tenuta sotto controllo da una persona competente - Per salvare l'output dobbiamo realizzare script al di fuori del nostro programma - Tutto l'output viene inviato nello stesso luogo: non c'è modo di separare per priorità o per sottosistema 37 Logging Un Log è un file di testo che raccoglie l'output da un programma È usato normalmente per segnalazione e per debug (es. server) Si vogliono raccogliere dati sull'uso ed il comportamento di determinati oggetti Si vuole avere un resoconto delle funzioni eseguite 38 Apache Log4J 2 È una libreria usata nei progetti Java È ormai uno standard de facto per il logging La seconda versione (Log4J 2) ha migliorato performance, funzionalità e affidabilità ma non è compatibile con la precedente (http://logging.apache.org/log4j) Le librerie minime da usare sono log4j-api e core (attualmente log4j-api-2.0-beta9.jar e core-2.0-beta9.jar) log4jlog4j39 Apache Log4J 2 E’ utile includere i JavaDoc Definisce diversi livelli di log a seconda della gravità: FATAL, ERROR, WARN, INFO, DEBUG, TRACE Un file di configurazione (XML o JSON) ci permette di scegliere la destinazione dei nostri log, cosa loggare e il formato del log. 40 Apache Log4J public class ProvaLog4J { private static Logger logger = LogManager.getLogger("Nome del logger"); public static void main(String[] args){ logger.fatal("Esempio di log fatal"); logger.error("Esempio di log error"); logger.info("Esempio di log info"); } } 41 Apache Log4J 2 File log4j2.xml (nel classpath – in src) <?xml version="1.0" encoding="UTF-8"?> <configuration status="OFF"> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %5level %logger{36} - %msg%n"/> </Console> </appenders> <loggers> <root level="all"> <appender-ref ref="Console"/> </root> </loggers> 42 </configuration> Apache Log4J 2 Esercitazione 1 43 Programmazione Java Avanzata e Mobile PATTERN Pattern Una soluzione progettuale generale a un problema ricorrente Elementi per il riuso di software ad oggetti Vantaggiosi e ‘’obbligatori’’ da usare Pattern Un pattern è caratterizzato da: Nome - rappresentativo del pattern stesso Problema - la situazione (o condizione) alla quale si può applicare il pattern Soluzione – la configurazione degli elementi adatta a risolvere il problema Conseguenze, vantaggi (risultati) e svantaggi (vincoli) che derivano dall'applicazione del pattern Pattern Importanti sono le conseguenze: Esse comprendono considerazioni di tempo e di spazio, possono descrivere implicazioni del pattern con alcuni linguaggi di programmazione e l'impatto con il resto del progetto Pattern Il pattern Singleton Pattern - Singleton Semplice da presentare e implementare (in Java), risulta molto utilizzato Nome: Singleton Problema: assicurare che per una determinata classe esista un’unica istanza attiva. Utile nel caso in cui si abbia la necessità di centralizzare informazioni e comportamenti in un’unica entità condivisa da tutti i suoi utilizzatori. Es. Spooler di stampa Pattern - Singleton Soluzione: associare alla classe stessa la responsabilità di creare le proprie istanze. Conseguenze: • accesso controllato alla singola istanza; • possibilità di usare un numero variabile di istanze; • una maggiore flessibilità (es. posso modificare i dati gestiti dal singleton senza invalidare l’unicità dell’istanza) Pattern - Singleton Implementazione in Java: public class Gestore { private static Gestore gestore = null; private Gestore() {} public static synchronized Gestore getGestore() { if (gestore == null) gestore = new Gestore(); return gestore; } } Pattern - Singleton Controllo dell’unicità: public static void main(String[] args) { Logger l = LogManager.getLogger("Test"); Gestore g1=Gestore.getGestore(); l.info(g1.toString()); Gestore g2=Gestore.getGestore(); l.info(g2.toString()); } Pattern Il pattern DAO (Data Access Object) Pattern - DAO Nome: DAO Problema: disaccoppiare l’accesso ai dati rispetto alla sua memorizzazione sottostante. Es. i metodi create(…), read(…), update(…), delete(…) usati per la persistenza dei dati (CRUD) andrebbero tutti modificati al cambiare della sorgente dei dati (passo da Oracle a MySQL) Pattern - DAO Soluzione: sposto la logica di accesso ai dati all’oggetto DAO che indica quali funzionalità sono supportate nascondendo la sorgente Conseguenze: portabilità delle applicazioni da una sorgente di dati ad un’altra posso iniziare l’implementazione senza dovermi occupare di quale sia la sorgente dei dati Pattern - DAO Implementazione in Java: scaricare esercitazione Dao Pattern Il pattern Factory (Factory Method) Pattern - Factory Nome: Factory Problema: creare oggetti senza specificare (esplicitamente) la classe specifica a cui gli oggetti appartengono. Soluzione: definire metodi ad hoc (factory) per la creazione degli oggetti. Conseguenze: rende completamente interscambiabili le implementazioni che soddisfano un’interfaccia Pattern - Factory Implementazione in Java: esercitazione Factory (Factory con Singleton) public class UtenteDAOFactory { private static UtenteDAO utenteDao = null; private UtenteDAOFactory(){} public static UtenteDAO getDAO() { if (UtenteDao == null ) { utenteDao = new UtenteDAOSimpleImpl(); } return utenteDao; } } Pattern - Factory Passi aggiuntivi - Modifico istanziazione del DAO (lo chiedo al Factory) UtenteDAO utenteDao = UtenteDAOFactory.getDAO(); - Modifico visibilità del costruttore dei DAOImpl protected UtenteDAOSimpleImpl(){}; - Pattern Il pattern MVC (separazione delle responsabilità) Pattern - MVC Scheda (molto) sintetica Nome: MVC (Model-Control-View) Problema: manutenibilità del codice (vedi prossime slide) Soluzione: suddivisione del codice in tre ‘’classi’’ distinte (vedi prossime slide) Conseguenze: promuove il riuso del codice, consente lo sviluppo del codice in parallelo, facilita l’implementazione di nuove funzionalità, e molte altre cose positive … Pattern - MVC Problema a cui si applica: esempio Consideriamo un sito e le pagine per gestire l’autenticazione di un utente. Una pagina risponde alla richiesta di login, elabora i dati, memorizza delle informazioni, e mostra il risultato della procedura (autorizzato o no). Una modifica ad una delle funzionalità sopra coinvolge tutte le altre funzionalità. Esempio Esempio: Vincoli Gli stessi dati aziendali devono essere presentati in più modi: HTML, WML, JFC/Swing, XML. Gli stessi dati aziendali devono essere aggiornati a seguito di differenti tipi di interazione: uso di link su una pagina HTML o WML, uso di pulsanti su una GUI JFC/Swing, uso di messaggi SOAP scritti in XML. Supportare molteplici tipi di presentazioni (view) ed interazioni non deve avere impatto sui componenti Problemi/1 Il mix fra il codice che riguarda la logica dell’applicazione (login, inserimento informazioni, ecc.) e quello che riguarda la presentazione dei risultati (HTML, XML, …) causa: Duplicazione di codice (relativo alla logica) in ogni nuova interfaccia (presentazione) che viene aggiunta all’applicazione. Difficoltà nel debug del codice e nella manutenzione. Problemi/2 Difficoltà nel mantenere costante la logica al variare della presentazione (presentazione logica). Se in futuro cambia la logica, si devono operare tante modifiche per quante volte essa è ripetuta nei moduli di presentazione (logica presentazione). L’implementazione di una pagina richiede competenze estese (Java, HTML, XML, SOAP, …) Soluzione: Model-View-Controller Model. Si occupa solo dei dati utili all’applicazione e fornisce i metodi per accedere ad essi. View. Si occupa solo della gestione dell’interfaccia utente. Non si preoccupa della logica applicativa o dell’elaborazione/validazione dell’input. Garantisce che l’interfaccia rifletta lo stato corrente del modello. Controller. Si occupa soltanto di validare/elaborare l’input dell’utente (fornito dalla parte View) e tradurlo in aggiornamenti (messaggi) che vengono passati al modello. Non si occupa di come sia ricevuto l'input (interfaccia) o di che cosa faccia il modello con questi aggiornamenti. Soluzione: Model-View-Controller Flusso di controllo MVC Model submit form Controller View 14 Pattern MVC Responsabilità del Model: Il model memorizza i dati e fornisce del metodi specifici per l’applicazione per definire i valori di questi. I metodi di gestione dei dati non sono generici. Sono personalizzati per l’applicazione e devono essere noti al controller e alla view. Il model deve anche fornire alle view un modo con cui queste possono registrarsi e annullare la loro registrazione. In tal modo, se determina che il suo stato è cambiato, deve inviare una notifica a tutte le viste registrate. Pattern MVC I dati potrebbero essere ovunque: File locale, File remoto, DBMS Oracle, DBMS MySQL, … Se sono su un DBMS, possiamo utilizzare Hibernate per implementare il Model. Hibernate facilita la memorizzazione (persistenza) e gestione di oggetti Java associati a relazioni di un DBMS. La suddivisione dell’applicazione in package dovrebbe rispecchiare l’implementazione del pattern MVC Pattern Il pattern Inversion Of Control (opzionale) Pattern - IoC Nome: Inversion Of Control Problema: la dipendenza tra le classi Il principio della separazione delle responsabilità (MVC) porta all’esplosione del numero delle classi (ognuna specializzata su una particolare responsabilità). Le classi arrivano ad accumulare dipendenza da decine di altre classi. Le istanze per essere inizializzate richiedono di crearne altre, e queste ancora altre, e così via Pattern - IoC Conseguenze delle dipendenze (RFI): 1. E’ complicato modificare una classe perchè tale cambiamento coinvolge altre classi (Rigidità) 2. Effettuando una modifica ad una classe, un’altra parte del sistema smette di funzionare (Fragilità) 3. E’ complicato riutilizzare moduli di un’applicazione perchè prima devono essere “sganciati” dal resto del codice (Immobilità) Pattern - IoC Esempio: public Class OggettoA { …} public Class OggettoB { private OggettoA a; OggettoB (){ this.a=new OggettoA(…); } … } OggettoB dipende da OggettoA Il costruttore genera un forte accoppiamento tra le classi Pattern - IoC Principi alla base della soluzione: 1. I moduli di alto livello non devono dipendere da quelli di basso livello: entrambi dovrebbero dipendere solo da astrazioni (interfacce) 2. Le astrazioni non dovrebbero dipendere dai moduli di basso livello, questi ultimi dovrebbero dipendere dalle astrazioni. Pattern - IoC Dependency Injection come soluzione: Es. Construction Injection - la dipendenza viene iniettata tramite l'argomento del costruttore public Class OggettoB { private OggettoA a; OggettoB (OggettoA a){ this.a=a; } … } Es. Setter Injection - la dipendenza viene iniettata attraverso un metodo "set" Pattern Il pattern Decorator (opzionale) Pattern - Decorator Nome: Decorator Problema: aggiungere funzionalità (Decorazioni) ad una classe (Component) a run-time Soluzione: - Definire un'interfaccia (astrazione) del Component, che le classi concrete implementano Definire una classe (Decorator) che rappresenta l a decorazione, implementa l'interfaccia Component e contiene l’oggetto che decora Pattern - Decorator Gelato (Componente), Decorazioni (panna, 3_gusti, max, …) public interface Gelato { public double getPrezzo();} public class GelatoBase implements Gelato { public double getPrezzo() {return 1;} } abstract public class GelatoDecorator implements Gelato { private Gelato decoratedGelato; public GelatoDecorator(Gelato decoratedGelato) { this. decoratedGelato = decoratedGelato; } public double getPrezzo() { return decoratedGelato.getPrezzo(); } } Pattern - Decorator public class ConPanna extends GelatoDecorator { public conPanna(Gelato decoratedGelato) { super(decoratedGelato); } public double getPrezzo() { return super.getPrezzo() + 0.2; } } public class Max extends GelatoDecorator { public Max(Gelato decoratedGelato) { super(decoratedGelato); } public double getPrezzo() { return super.getPrezzo() + 0.7; } } … Pattern - Decorator Gelato g = new GelatoBase(); System.out.println(“Prezzo: " + g.getPrezzo()); g = new conPanna(g); //Gelato con panna System.out.println(“Prezzo: " + g.getPrezzo()); g = new Max(g); //Gelato Max con Panna System.out.println(“Prezzo: " + g.getPrezzo()); …