Programmazione a Oggetti Modulo B Lezione 5 Dott. Alessandro Roncato 08/11/2011
Riassunto Pattern Singleton Pattern Plolimorfismo Classi e Interfacce 2
Ereditarietà multipla In Java una classe può estendere una sola altra classe, mentre può implementare tante interfacce In altri linguaggio possiamo avere l'estensione multipla (ovvero la possibilità che una classe estenda tante altre classi) In ogni caso l'ereditarietà multipla è fonte di problemi 3
Interfacce multiple <<interface>> BeneInventariato <<interface>> Veicolo <<implements>> <<implements>> Automobile OK 4
Ereditarietà multipla BeneInventariato Veicolo Automobile NO! 5
Codice (se si potesse) public class B { public methodb1(){...}; public methodb2(){...}; } public class C { public methodc1(){...} public methodc2(){...} } public class A extends B C { 6
Uso classe public class XXX { A a public void peresempio(){ a.methodb1(); a.methodc1(); B b = a; b.methodb2(); C c = a; c.methodc2(); } 7
Ereditarietà multipla Come ottenere l'ereditarietà multipla in Java? Supponiamo che A erediti dalle classi B, C, D etc. A estende una sola delle classi (meglio la classe con più metodi pubblici) Le altre classi vengono incorporate nella classe A Servono delle interfacce IB, IC, etc 8
Ereditarietà multipla B C A 9
Ereditarietà singola +... B <<interface>> IC <<implements>> A C c: IC 10
Codice public interface IC { methodc1(); methodc2(); } public class C implements IC { public methodc1(){...} public methodc2(){...} } public class A extends B implements IC { IC c = new C(); } public methodc1(){c.methodc1();} //wrapper public methodc2(){c.methodc2();} //wrapper 11
Uso classe public class XXX { A a public void peresempio(){ a.methodb1(); a.methodc1(); B b = a; b.methodb2(); IC c = a; c.methodc2(); } 12
Pattern Nelle lezioni vediamo come applicare i pattern in alcuni punti della progettazione Gli stessi pattern possono essere applicati anche in altre parti della progettazione E' parte del lavoro di progetto considerare tutti i punti in cui un pattern può essere applicato. 13
Tipi Pattern Creazionali (diagrammi classi e sequenza) Strutturali (diagramma classi) Comportamentali (diagramma sequenza) Concorrenza (diagramma attività) 14
Ricerca di un prodotto La responsabilità di gestire la ricerca può essere gestita in due modi: 1) da uno (o più) metodo(i) di una classe esistente 2) da una classe a se stante. 15
Classe esistente Un oggetto globale (visibile globalmente) gestisce la ricerca Per esempio l'oggetto Negozio può gestire la ricerca Problemi : 1)bassa coesione 2)una ricerca per ogni oggetto a cui è associata (potrebbero essere poche o troppe!). Se associata al Negozio, abbiamo un'unica ricerca. 16
public class Negozio { Esempio Set<Prodotto> risultatoricerca;... public Negozio(){ }...//inizializzazione negozio // piu' // inizializzazione ricerca //metodi Negozio più metodi ricerca public void cercaprodotti(string query){...} Bassa coesione Difficile il riuso Alta dipendenza public Set<Prodotto> getricerca(){ return risultatoricerca();} } public void raffinaricerca(string query){...} 17
Svantaggi Bassa coesione: cosa hanno a che fare i metodi di Negozio con quelli della ricerca dei Prodotti? Riuso difficile: come posso riusare le funzionalità della ricerca senza la classe Negozio? Dipendenza: aumentano le classi che hanno bisogno di accedere alla classe Negozio (Negozio+ chi deve accedere alle ricerche) 18
Pure Fabrication P: come assegnare le responsabilità in modo da ottenere Low Coupling and High Coesion se I.E. NON è appropiato? S: assegna un insieme altamente coeso di responsabilità a una classe artificiale (di pura invenzione) che non rappresenta un concetto del dominio del problema, ma qualcosa di inventato per sostenere H.C., L.C. e il riuso. 19
Esempio Facciamo gestire le ricerche da una nuova classe inventata Ricerca (Questa classe non è proprio una pura invenzione perché come concetto la Ricerca esiste) Vedremo poi altri esempi in cui inventiamo nuove classi più di sana pianta 20
public class Ricerca { Set<Prodotto> risultato; Esempio public void cercaprodotti(string query){...} public Set getricerca(){return risultatoricerca(); public void raffinaricerca(string query){...} } } Alta coesione Riuso Riduce dipendenza 21
Pure Fabrication Ricerca * 1 Negozio Cassiere 1 * Prodotto * 0..1 Carrello 22
Pure Fabrication Ricerca * 1 Negozio Cliente 1 * Prodotto * 0..1 Carrello 23
Altri esempi Chi è responsabile della persistenza degli oggetti? Persistenza=salvare, leggere, aggiornare, cercare gli oggetti nel filesystem o in un DB Per I.E. Dovrebbe essere l'oggetto stesso a farlo Ma questo ha gli svantaggi già visti Si preferisce una Pure Fabrication che gestisca la persistenza di tutti gli oggetti 24
Altri esempi Chi è responsabile della di gestire la visualizzazione? Per I.E. Dovrebbe essere l'oggetto stesso a farlo Ma questo ha gli svantaggi già visti Si preferisce una Pure Fabrication che gestisca la visualizzazione degli oggetti 25
Svantaggi Pure Fabrication Una classe in più Potrebbe essere difficile da individuare la classe responsabile Visione procedurale invece che ad oggetti (raggruppo per funzionalità e non per concetto) (vedremo come limitare questo ultimo problema) 26
Chi crea gli oggetti letti dal DB? public class Prodotto { static public Prodotto load(int id){ Class.classForName( Driver ); Connection con=drivermanager.get.conn... Statement sta = con.createstatement(); ResultSet rs = sta.executequery(... if (rs.next){ String codice=rs.getstring( codice ); return new Prodotto(codice,... } } 27
Chi crea gli oggetti letti dal DB? public class Cliente { static public Cliente load(int id){ Class.classForName( Driver ); Connection con=drivermanager.get.conn... Statement sta = con.createstatement(); ResultSet rs = sta.executequery(... if (rs.next){ String nome=rs.getstring( nome ); return new Cliente(nome,... } } 28
Svantaggi Logica di creazione complessa Poca coesione Forte dipendenza degli oggetti del modello da le classi di gestione del DB Ripetizione di codice in classi diverse (vedi esercizio più avanti) 29
Soluzione Specializzazione di Pure Fabrication: Factory (o anche detto Simple Factory o Concrete Factory) Un oggetto di pura invenzione crea gli oggetti del modello Attenzione: Factory=Fabbrica Fabrication=Invenzione 30
Pattern Factory P: chi deve creare gli oggetti quando la creazione è complessa? S: Un oggetto di pura invenzione chiamato Factory 31
Factory public class ProdottoFactory { public Prodotto load(int id){ Class.classForName( Driver ); Connection con=drivermanager.get.connection(...); Statement sta = con.createstatement(); ResultSet rs = sta.executequery(... ); if (rs.next){ String codice=rs.getstring( codice ); return new Prodotto(codice,...); } } } 32
Factory public class ClienteFactory { public Cliente load(int id){ Class.classForName( Driver ); Connection con=drivermanager.get.connection(...); Statement sta = con.createstatement(); ResultSet rs = sta.executequery(... ); if (rs.next){ String nome=rs.getstring( nome ); return new Cliente(nome,...); } } } 33
Uso Factory public class XXX { public qualchemetodo() { Prodotto prodotto=...load(id); } } //cosa metto al posto dei...? //quante Factory sono necessarie? //che visibilità devono avere? //vedi esercizio 34
Considerazioni Una classe in più per ogni classe del Modello Più coeso Facile il riuso? 35
Factory ProdottoFactory XXX Prodotto 36
Esercizi Cosa metto al posto dei puntini...? Che vantaggi e svantaggi ci sono? Come posso ottenere alta coesione e bassa dipendenza usando static invece che Factory? Che vantaggi e svantaggi ci sono? 37
Factory Method Nell'esempio di Simple Factory abbiamo visto che la Factory definisce il metodo load che è un esempio Factory Method. Possibilità di avere nomi significativi per i metodi (es. loadfromdb, loadfromfile, createnew) (invece il costruttore ha un nome imposto) 38
Factory method public class Prodotto { private Prodotto() { } public static Prodotto createnew(){ return new Prodotto(); } public static Prodotto loadfromdb(int id){...//come già visto } } 39
Abstract Factory A differenza del Simple Factory che è una classe, l'abstrac Factory definisce un'interfaccia Ci vogliono quindi almeno una classe e un'interfaccia (prima avevo un'unica classe) L'interfaccia definisce almeno un factory method che poi verrà implementato da una o più classi 40
Abstract Factory <<interface>> AbstractProdottoFactory <<implements>> ProdottoFactory XXX Prodotto 41
Abstract Factory public interface AbstractProdottoFactory { public Prodotto createnew(); } 42
Abstract Factory public class ProdottoFactory implements AbstractProdottoFactory { public Prodotto createnew(){... } } 43
Uso Abstract Factory public class XXX { public void qualchemetodo() { Prodotto prodotto=...createnew(); //cosa metto al posto dei...? //la differenza rispetto a Simple Factory //dipende da cosa metto nei puntini } } } 44
Esercizio Fare in modo di usare Polimorfismo e ConcreteFactory in modo che il codice di accesso al DB venga riusato quanto più possibile. Nella applicazioni reali si usa anche la Reflection in modo da evitare addirittura la necessità delle sottoclassi e che Factory dipenda dagli oggetti della nostra applicazione 45
Polimorfismo + Factory ClienteFactory Cliente Factory ProdottoFactory Prodotto ScontrinoFactory Scontrino...... 46
Reflection + Factory Cliente Factory Prodotto Scontrino... 47
Reflection Reflection viene spesso usato assieme ad altre tecniche per ridurre la dipendenza Vediamo come sia possibile cercare di ridurre la dipendenza anche altro modo. 48
Protect Variations Come proteggere un oggetto/sistema dai cambiamenti indotti dagli altri oggetti/sistemi? Mascherando i cambiamenti degli altri oggetti tramite una inter-faccia stabile Una tecnica per mascherare è la progettazione guidata dai dati e un caso particolare è un file di configurazione 49
Come ridurre dipendenza Come possiamo sfruttare un file di configurazione per ridurre la dipendenza? Mettiamo nel file di configurazione tutti i tipi per la creazione dei libri 50
Dipendenze prima Negozio AlimentareFac. Alimentare Factory ElettroFact. Elettrodom. Prodotto RivistaFactory Rivista 51
Dipendenza Il fatto che Negozio costruisca tramite le varie Factory tutti i prodotti crea una forte dipendenza di Negozio: c'é più possibilità che Negozio debba essere modificata a causa di un cambiamento alla costruzione di un oggetto Prodotto che per una modifica al funzionamento del Negozio stessa 52
Dipendenze dopo java.io.file java.io.bufferedreader java.util.stringtokenizer Negozio AlimentareFact Alimentare Factory ElettroFact. Elettrodom. Prodotto RivistaFactory Rivista 53
Dipendenze dopo java.io.fileinputstream java.util.properties Negozio AlimentareFact Alimentare Factory ElettroFact. Elettrodom. Prodotto RivistaFactory Rivista 54
Pro e contro +/- Tolgo dipendenze ma ne Aggiungo altree + Ma le dipendenza con classi delle API standard di Java sono meno problematiche perché molto stabili (pochi cambiamenti in vista) - Non tutto il comportamento risiede nel codice, bisogna sapere dell'esistenza del file esterno 55
Formato file.properties #commenti preceduti dal carattere #!commenti preceduti dal carattere! number=23; stringa=questa è una stringa stringasumoltelinee=una stringa \ su più linee altrastringa:quaranta stringaconuguale=x\=y stringaconslash=x\\y stringacon2punti=x\:y nomecon\:=valore 56
try { FileInputStream fis = Codice new FileInputStream( negozio.properties ); Properties props = new Properties(); props.load(fis); fis.close(); String tempnum=props.getproperty( numeroprodotti ); int num = Integer.parseInt(tempNum); for (int i=0; i<num; i++) { Prodotto prodotto=new Prodotto(); prodotto.setcodice(props.getproperty( codice +i)); double prezzo = Double.parseDouble(props.getProperty( prezzo +i)); 57
File negozio.properties #negozio.properties # numeroprodotti=4 codice0=1234567890123 prezzo0=12.34... codice1=0123456789012 prezzo1=43.21 codice2=2345678901234 Prezzo2=23.41... codice3=3216549872103 prezzo3=32.14 58
Cosa manca? Generare i Prodotti in maniera polimorfa! In pratica dobbiamo riuscire a creare i prodotti 0 e 1 di tipo Alimentare, mentre il prodotto 2 di tipo Elettrodomestico e il 3 di tipo Rivista Serve di nuovo la Reflection La reflection è una tecnica spesso usata per implementare P.V. 59
File negozio.properties #negozio.properties # numeroprodotti=4 codice0=1234567890123 prezzo0=12.34 class0=negozio.alimentare... codice1=0123456789012 prezzo1=43.21 class1=negozio.alimentare codice2=2345678901234 prezzo2=23.41 class2=negozio.elettrodomestico codice3=3216549872103 Prezzo3=32.14 class3=negozio.rivista 60
try { FileInputStream fis = Codice new FileInputStream( negozio.properties ); Properties props = new Properties(); props.load(fis); fis.close(); String tempnum=props.getproperty( numeroprodotti ); int num = Integer.parseInt(tempNum); for (int i=0; i<num; i++) { Prodotto prodotto = Class.forName(props.getProperty( class +i)).newinstance(); prodotto.loadfromproperties(props,i); } 61
Esercizio Perché abbiamo richiamato il metodo loadfromproperties? A cosa serve il parametro i di questo metodo? 62
Protect Variation Più complesso Più lento Più costoso Senza controllo compilatore (vedi try-catch) Bisogna valutare se effettivamente l'interfaccia rappresenta un punto di possibile cambiamento La modifica può essere fatta senza bisogno di modificare il codice Java 63
Esercizio Progettare e implementare una Simple Factory con Protect Variation per la creazione dei Prodotti (elencare pro e contro) Negozio Prodotti ProdottiFactory java.io.fileinputstream java.util.properties 64
Esercizio Pensare a dover modificare l'applicazione dovendo aggiungere nuovi tipi di Prodotti. Quale delle scelte viste risulta la migliore? 65
Evoluzioni progettisti Alle prime esperienze, i progettisti tendono a essere troppo ottimisti e pensare che non ci saranno variazioni, bug e manutenzione e quindi la struttura del codice risulta troppo sensibile ai cambiamenti Successivamente, i progettisti tendono a essere troppo pessimisti e prevedono troppi punti di cambiamento e il codice risulta troppo complesso 66
Riflessioni sulla reflection La reflection è molto interessante e permette di ottenere codice molto flessibile Analoga alla reflection sono i Metadati per quanto riguarda i DB Attenzione che usando la reflection perdiamo i vantaggio di avere un linguaggio fortemente tipato e spostiamo al run-time la scoperta di possibili errori. 67
Riflessioni sulla reflection 2 Tramite reflection è possibile cambiare al run-time la visibilità dei membri di una classe: è quindi possibile accedere ad un attributo che è privato Questo permette a molte librerie/framework di accedere in modo uniforme allo stato degli oggetti Il problema della mancanza di controllo del compilatore viene mitigato dal fatto che il codice è ben testato dai tanti utilizzatori 68
Reflection e sicurezza Vale la pena di rimarcare che il fatto che tramite la reflection sia possibile cambiare la visibilità di attributi di una classe, ancora di più sottolinea che l'unico scopo delle visibilità private, protected e package è quello di limitare la dipendenza tra le classi e NON riguarda minimamente aspetti di sicurezza applicativa. Quindi: per nascondere le password non ha senso usare private 69
Sicurezza e Java Spesso si sente dire che Java è un linguaggio sicuro a differenza del C. Questa è vero ma NON riguarda il fatto che C non ha le visibilità private, protected etc. Bensì, riguarda i controlli fatti al run time dalla virtual machine quando esegue il nostro codice (es. controllo indici, SecurityManager, Eccezioni etc.) 70