MegaRiassunto Programmazione 3 PDF - Tartaglia Luca

Summary

Questo documento, creato da Tartaglia Luca, è un riassunto dettagliato di Programmazione 3, coprendo argomenti chiave come l'ereditarietà, il polimorfismo e i design patterns. Il riassunto include anche una trattazione dei principi di OOD e SOLID, essenziali per la programmazione a oggetti.

Full Transcript

BY TARTAGLIA LUCA 🐼 Programmazione 3 - Sommario Ereditarietà e Interfacce........................................................................................................................................................ 3 Ereditarietà..........................................................

BY TARTAGLIA LUCA 🐼 Programmazione 3 - Sommario Ereditarietà e Interfacce........................................................................................................................................................ 3 Ereditarietà......................................................................................................................................................................... 3 Interfacce............................................................................................................................................................................ 3 Classi astratte vs Interfacce............................................................................................................................................... 4 Polimorfismo.......................................................................................................................................................................... 5 Polimorfismo per metodi................................................................................................................................................... 5 Polimorfismo per dati........................................................................................................................................................ 6 Gestione delle eccezioni........................................................................................................................................................ 8 Tipologie di classi e annotazioni............................................................................................................................................ 9 Principi di OOD..................................................................................................................................................................... 10 Principi del Software Design............................................................................................................................................ 10 Principi SOLID................................................................................................................................................................... 10 Design Patterns.................................................................................................................................................................... 13 Pattern creazionali........................................................................................................................................................... 13 Singleton....................................................................................................................................................................... 13 Factory Method (o costruttore virtuale, “metodo fabbrica”).................................................................................... 14 Abstract Factory........................................................................................................................................................... 15 Factory.......................................................................................................................................................................... 16 Builder.......................................................................................................................................................................... 16 Prototype...................................................................................................................................................................... 17 Pattern comportamentali................................................................................................................................................ 18 Chain of Responsability (CoR)...................................................................................................................................... 18 Command..................................................................................................................................................................... 18 Interpreter.................................................................................................................................................................... 20 Iterator.......................................................................................................................................................................... 21 Mediator....................................................................................................................................................................... 22 Memento...................................................................................................................................................................... 23 Observer....................................................................................................................................................................... 23 State.............................................................................................................................................................................. 24 Strategy........................................................................................................................................................................ 26 Template Method........................................................................................................................................................ 26 Visitor........................................................................................................................................................................... 27 Pattern strutturali............................................................................................................................................................ 28 Adapter......................................................................................................................................................................... 28 Composite..................................................................................................................................................................... 29 Bridge............................................................................................................................................................................ 30 Decorator...................................................................................................................................................................... 31 BY TARTAGLIA LUCA 🐼 Facade........................................................................................................................................................................... 32 Flyweight...................................................................................................................................................................... 32 Proxy............................................................................................................................................................................. 33 Model View Controller..................................................................................................................................................... 35 Data Access Object........................................................................................................................................................... 36 Gestione dei Thread............................................................................................................................................................. 37 BY TARTAGLIA LUCA 🐼 Ereditarietà e Interfacce Ereditarietà L’ereditarietà è un concetto importante nella programmazione in Java che permette di creare una nuova classe a partire da una classe esistente. La nuova classe è chiamata sottoclasse e la classe da cui è derivata è chiamata superclasse. La sottoclasse eredita automaticamente i membri (metodi e variabili) della superclasse, ma può anche ridefinirli o definirne dei nuovi. Un metodo ridefinito (o sovrascritto) ha la stessa firma (nome e parametri) di un metodo della superclasse. Una variabile privata della superclasse non può essere modificata direttamente da una sottoclasse, ma solo tramite un metodo pubblico della superclasse. Il modificatore final impedisce di ridefinire metodi o estendere classi. Tutte le classi in Java estendono la classe Object, che rappresenta un oggetto generico. La classe Object include i metodi toString, equals e clone. Il controllo di accesso ai membri di una classe può essere public, private, protected o di pacchetto. "Public" significa che il membro è accessibile da qualsiasi parte del programma. "Private" significa che il membro è accessibile solo all’interno della classe in cui è definito. "Protected" significa che il membro è accessibile solo all’interno della classe e delle sue sottoclassi. "Pacchetto" (o senza modificatore di accesso) significa che il membro è accessibile solo all’interno del pacchetto (insieme di classi) in cui la classe è definita. Questi modificatori di accesso sono utilizzati per garantire la coerenza delle informazioni e la sicurezza dei dati all’interno della classe e delle sue relazioni con le altre classi. Un metodo astratto è un metodo che non ha implementazione e deve essere ridefinito da una sottoclasse. Una classe astratta è una classe che contiene almeno un metodo astratto e non può essere istanziata. public abstract Class Pittore { //… public abstract void dipingiQuadro(); //… } L'incapsulamento è una tecnica che permette di nascondere i dettagli di implementazione di una classe e mostrare solo l’interfaccia pubblica (si tratta di rendere privata una variabile e consentire l’accesso ad essa soltanto tramite getter e setter). Questo migliora la sicurezza e la manutenibilità del codice. Interfacce Un’interfaccia è un’evoluzione del concetto di classe astratta in Java, che non può essere istanziata e che richiede di essere estesa da una classe. Nelle interfacce tutti i metodi sono astratti e pubblici, senza variabili istanza. Per realizzare un’interfaccia, una classe deve fornire tutti i metodi richiesti. In Java 8, le interfacce hanno acquisito maggiore potenza e possono avere metodi statici e metodi concreti di default. Una classe può implementare più interfacce ed ereditare solo la loro parte funzionale (i metodi) e non i dati. Nel caso di conflitto di nomi di metodi tra due interfacce, è necessario che la classe che le implementa risolva il conflitto ridefinendo il metodo (diamond problem) o ereditando l’implementazione della interfaccia più specifica. BY TARTAGLIA LUCA 🐼 Classi astratte vs Interfacce In Java, una classe può implementare molte interfacce, ma può ereditare solo da una classe astratta. Una classe astratta può avere metodi non astratti, mentre tutti i metodi di un’interfaccia sono astratti. Una classe astratta può avere variabili di istanza che verranno ereditate dalle sottoclassi, mentre un’interfaccia può solo avere campi static final. Le classi astratte possono avere costruttori, ma le interfacce no. I metodi di una classe astratta possono essere di visibilità protected, private o non specificata, mentre tutti i metodi di un’interfaccia sono public. La classe astratta eredita da Object, che include metodi come clone() ed equals(). BY TARTAGLIA LUCA 🐼 Polimorfismo Il polimorfismo in Java è la capacità di utilizzare un unico termine per riferirsi a diverse "entità". C'è polimorfismo per metodi, che comprende l’overload (metodi con lo stesso nome ma con diverse firme) e l'override (ridefinizione di un metodo nella sottoclasse), e polimorfismo per dati, che comprende il riferimento di una superclasse a un’istanza di una sottoclasse. Il polimorfismo per dati consente anche di gestire collezioni eterogenee e di effettuare il cast tra oggetti. Polimorfismo per metodi L'overload (sovraccarico) è la possibilità di avere più metodi nella stessa classe con lo stesso nome ma con una firma diversa (cioè un diverso numero o tipo di argomenti). Questo permette di definire più comportamenti per un unico metodo in base agli argomenti che riceve. Esempio public class Example { public int add(int a, int b) { return a + b; } public float add(float a, float b) { return a + b; } public double add(double a, double b) { return a + b; } } L'override (ridefinizione) è la possibilità di riscrivere un metodo ereditato da una superclasse nella classe figlia. Questo permette di cambiare il comportamento del metodo ereditato in base alle esigenze della classe figlia. La firma del metodo ridefinito deve essere uguale a quella del metodo originale e il modificatore di accesso non deve essere più restrittivo. L'annotazione "@Override" viene utilizzata per specificare al compilatore che si sta effettivamente ridefinendo un metodo e per evitare errori. Esempio class Animal { public void makeSound() { System.out.println("Animal sound"); } } class Dog extends Animal { @Override public void makeSound() { System.out.println("Bark"); } } BY TARTAGLIA LUCA 🐼 Polimorfismo per dati Il polimorfismo per dati è una caratteristica della programmazione ad oggetti che permette di trattare un’istanza di una classe figlia come se fosse un’istanza della sua classe padre. Ciò significa che è possibile assegnare un riferimento alla classe padre ad un’istanza della classe figlia, anche se il dato è stato istanziato come una classe figlia. Questo è possibile perché il valore che si legge corrisponde all’implementazione interna, indipendente dalla rappresentazione del dato. Ad esempio, un metodo che ha un parametro polimorfo, cioè un parametro di tipo reference, può puntare ad un oggetto istanziato da una classe figlia. Il metodo println(), ad esempio, prende un parametro di tipo Object, che è la classe padre di tutte le classi in Java. Esempio class Animale { void faiQualcosa() { System.out.println("L'animale sta facendo qualcosa"); } } class Gatto extends Animale { void faiQualcosa() { System.out.println("Il gatto sta facendo le fusa"); } } class Cane extends Animale { void faiQualcosa() { System.out.println("Il cane sta abbaiando"); } } public class Esempio { public static void main(String[] args) { Animale animale = new Gatto(); animale.faiQualcosa(); animale = new Cane(); animale.faiQualcosa(); } } Inoltre, la possibilità di utilizzare collezioni eterogenee, che sono collezioni composte da oggetti diversi, è garantita dal polimorfismo per dati. Object arr[] = {new Punto(), “Hello World!”, new Date()}; L’operatore instanceof permette di testare a quale tipo di istanza punta un riferimento. Restituisce vero se il primo operando è un riferimento che punta ad un oggetto istanziato dal secondo operando o ad un oggetto istanziato da una classe figlia. BY TARTAGLIA LUCA 🐼 if (dipendente instanceof Programmatore) … Il casting di oggetti è il processo di modificare il tipo di un oggetto in modo che sia possibile accedere ai membri della classe specifica a cui appartiene. … Programmatore programmatore = (Programmatore) dipendente; L’invocazione virtuale di un metodo si verifica quando un metodo ridefinito (overridden) in una sottoclasse viene invocato su un’istanza di quella sottoclasse tramite un reference alla superclasse. In questo caso, il metodo ridefinito nella sottoclasse viene invocato e non quello definito nella superclasse. Object obj = new Date(); String s1 = obj.toString(); Se dovessimo fare un confronto con il polimorfismo per metodi potremmo dire che quest’ultimo è simile, ma si riferisce alla possibilità di invocare metodi diversi su oggetti diversi attraverso un reference alla stessa interfaccia o classe padre. BY TARTAGLIA LUCA 🐼 Gestione delle eccezioni Le eccezioni sono situazioni in cui il flusso normale del programma viene deviato. Vengono gestite da un blocco di codice apposito chiamato "gestore di eccezioni". Questo blocco determina come correggere o recuperare dall’errore. Per creare un’eccezione, si estende una classe o un metodo con la classe Throwable che include alcune eccezioni predefinite. Per gestire le eccezioni, si utilizza il blocco Try/Catch: se si verifica un errore all’interno del blocco try, viene catturato e gestito. Quando il programma genera un’eccezione, viene restituito lo stack trace, che mostra la fonte dell’errore. Le eccezioni possono essere controllate o incontrollate. Le eccezioni controllate vengono segnalate dal compilatore prima dell’esecuzione del codice. Le eccezioni incontrollate si verificano a runtime e possono causare la terminazione improvvisa del programma. Gli errori, al contrario, rappresentano situazioni in cui il programma non può continuare a funzionare correttamente e quindi termina. Le asserzioni sono utilizzate per verificare la validità di determinate condizioni in fase di sviluppo, e se un’asserzione non viene soddisfatta, il programma termina immediatamente con un errore. Esempio public class AssertionExample { public static void main(String args[]) { int number=10; assert (number > 0); System.out.println("Number is positive"); } } La progettazione per contratto (design by contract) implica la specifica di precondizioni, postcondizioni e invarianti che descrivono gli stati validi di un’applicazione, al fine di garantirne la corretta esecuzione. BY TARTAGLIA LUCA 🐼 Tipologie di classi e annotazioni In Java esistono diverse tipologie di classi che possono essere utilizzate per organizzare il codice. Classe innestata: una classe definita all’interno di un’altra classe. Ha accesso ai membri della classe esterna, anche se dichiarati privati. Può essere dichiarata anche all’interno di un metodo. Se viene dichiarata statica, diventa una classe di livello superiore e non ha accesso alle variabili d’istanza della classe esterna. Classe anonima: una classe innestata senza nome. Utilizzata per fare override di uno o più metodi della classe che estende. Esempio public class AnonymousClassExample { public static void main(String[] args) { JButton button = new JButton("Click me"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Button clicked"); } }); } } Tipi enumerazioni: una classe che rappresenta una lista di valori costanti. Viene trasformata dal compilatore in una classe che estende la classe astratta Enum. Esempio public enum MiaEnumerazione { UNO, DUE, TRE; } Interfacce: un insieme di dichiarazioni di metodi che descrivono un’interfaccia comune. Non può contenere costruttori. Tutte queste classi possono essere utilizzate per organizzare il codice e gestire l’ereditarietà e il polimorfismo. Le annotazioni in Java sono informazioni aggiuntive che si possono aggiungere a classi, interfacce, enumerazioni e altri elementi del codice per fornire informazioni supplementari al compilatore e/o al runtime. Possono essere utilizzate per diverse finalità, come specificare il comportamento del codice durante la compilazione, il runtime o la generazione della documentazione. Ci sono diversi tipi di annotazioni in Java, come annotazioni standard, meta-annotazioni, annotazioni marcatrici, annotazioni a valore unico, annotazioni a più valori e annotazioni di tipo. Le meta-annotazioni, come @Retention e @Target, vengono utilizzate per annotare altre annotazioni e specificare come queste devono essere trattate dall’ambiente Java. Le annotazioni standard, come @Override e @Deprecated, forniscono informazioni sul comportamento del codice durante la compilazione e il runtime. Le annotazioni di tipo, disponibili a partire da Java 8, permettono di annotare non solo classi e interfacce, ma anche i tipi utilizzati in altri elementi del codice. BY TARTAGLIA LUCA 🐼 Principi di OOD Principi del Software Design I principi del Software Design (SD) rappresentano una linea guida per evitare di avere una cattiva progettazione. Una cattiva progettazione è caratterizzata da: Rigidità (se un sistema è difficile da cambiare poiché ogni cambiamento si ripercuote su molte parti) Fragilità (quando il cambiamento di una parte del sistema comporta la rottura di una parte inaspettata) Immobilità (quando è difficile riutilizzare qualche componente in un’altra applicazione perché non può essere estrapolata dall’applicazione corrente) Viscosità (quando l’ambiente di sviluppo è tenuto insieme in modo inappropriato) Complessità inutile (trattasi di strutture di codice non necessarie nel presente, ma nel futuro) Ripetizioni inutili Questi problemi possono essere causati da dipendenze mal gestite (spaghetti code). I linguaggi a Oggetti (OO) possono aiutare a gestire queste dipendenze attraverso l’utilizzo di interfacce e polimorfismo. Il Refactoring è una tecnica per modificare la struttura interna del codice senza modificarne il comportamento esterno, con l’obiettivo di migliorare la leggibilità, la manutenibilità, la riusabilità, l’estendibilità e la riduzione della complessità. È un elemento importante di molte metodologie di sviluppo del software emergenti, come Agile, Extreme Programming e Test Driven Development. Il refactoring si utilizza quando si ripete un’azione per la terza volta, si aggiunge una nuova caratteristica, si corregge un bug o si rivede il codice. Principi SOLID SOLID è un acrostico che si riferisce ai primi cinque principi di sviluppo del software: Single Responsibility Principle, Open-Closed Principle, Liskov Substitution Principle, Interface Segregation Principle e Dependency Inversion Principle. Questi principi forniscono linee guida per lo sviluppo di software estendibile e manutenibile. Il Single Responsibility Principle (SRP) sostiene che ogni elemento di un programma (classe, metodo, variabile) dovrebbe avere una sola responsabilità, che dovrebbe essere interamente incapsulata da quell’elemento. La soluzione sarebbe quella di separare i compiti in classi differenti. BY TARTAGLIA LUCA 🐼 L'Open-Closed Principle (OCP) sostiene che le entità software (classi, moduli, funzioni) dovrebbero essere aperte per l’estensione, ma chiuse per le modifiche. Questo significa che se vogliamo estendere una classe, dobbiamo farlo tramite l’aggiunta di nuove funzionalità, senza dover modificare il codice esistente; la soluzione sarebbe quella di utilizzare classi concrete e classi astratte. Il Liskov Substitution Principle (LSP) sostiene che i sostituti di un oggetto dovrebbero comportarsi in modo compatibile con l’oggetto originale, in modo da non causare problemi al resto del sistema. In parole povere è un’estensione dell’OCP: un oggetto di una sottoclasse dovrebbe poter essere utilizzato al posto di un oggetto della classe padre, senza causare problemi di esecuzione. Il Interface Segregation Principle (ISP) sostiene che le interfacce dovrebbero essere molte, piccole e specifiche piuttosto che poche, generali e grandi, in modo da non obbligare gli utenti a implementare funzionalità inutili (e dover ricompilare ogni volta più codice). Un client non dovrebbe dipendere dai metodi che non usa. BY TARTAGLIA LUCA 🐼 Il Dependency Inversion Principle (DIP) sostiene che i moduli di alto livello non devono dipendere dai moduli di basso livello. Entrambi devono dipendere dalle astrazioni, compresi i dettagli. Quindi bisogna evitare che la logica complessa dipenda dalle operazioni primarie. Il Law of Demeter (LoD) sostiene che ogni unità di programma dovrebbe conoscere solo poche unità di programma strettamente correlate e che ogni unità dovrebbe interagire solo con quelle che conosce direttamente. Questo significa che un oggetto non dovrebbe interagire direttamente con oggetti a cui accedere solo indirettamente, ma piuttosto utilizzare il metodo fornito direttamente dalla classe di quell’oggetto. Esempio objectA.getObjectB().doSomething(); BY TARTAGLIA LUCA 🐼 Design Patterns Il pattern è una soluzione architetturale utilizzata per risolvere problemi in contesti diversi. È stata la Gang of Four (GOF) a formalizzare il concetto con il libro “Design Patterns: elements of reusable object oriented software”, che include una collezione di 23 pattern. I design pattern possono essere di tre tipi: - Creazionali (Singleton, Factory Method, Abstract Factory, Factory, Builder, Prototype) - Strutturali (Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy) - Comportamentali (Chain of responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor) Pattern creazionali I pattern creazionali nascondono i costruttori ed originano i metodi. In questo modo possono utilizzare oggetti senza sapere come sono implementati. Singleton È uno tra i pattern più semplici, permette di creare una singola istanza di oggetto per volta attraverso un metodo, senza dunque definire un’istanza di classe. Per fare ciò rendiamo privato il costruttore e tramite un metodo pubblico si richiama l’unico oggetto creato. Singleton singleton = singleton.getInstance(); La tecnica sfruttata è quella della lazy initialization (inizializzazione lenta): sfrutta un flag di controllo in modo che, dopo la prima chiamata, venga restituita sempre la stessa istanza della classe singleton. Esempi di applicazione Classe login: prevede un punto di accesso generale per tutte le applicazioni Configuration classes: configura parametri per un’applicazione Accesso a risorse condivise: come se si trattasse di un’applicazione che usa una porta seriale in un ambiente multithreading, che può essere usato per gestire le operazioni sulla porta seriale Factory: spesso il pattern Singleton è associato al pattern Factory BY TARTAGLIA LUCA 🐼 Factory Method (o costruttore virtuale, “metodo fabbrica”) Il pattern Factory Method permette di creare oggetti senza conoscerne i dettagli (senza specificare la loro classe concreta), grazie all’utilizzo di un creator che saprà che oggetto restituire in base alle informazioni ricevute a run-time. Questo pattern viene utilizzato quando non è possibile prevedere in anticipo quale classe debba essere utilizzata per creare un oggetto, ma si vuole lasciare la scelta a sottoclassi "helper". Cosa fanno: Factory dichiara la Factory che dovrà restituire l’oggetto più appropriato ConcreteFactory effettua l’override del metodo della Factory per restituire l’implementazione dell’oggetto Product è l’interfaccia dell’oggetto che Factory dovrà creare ConcreteProduct implementa l’oggetto in base ai metodi dell’interfaccia Ad esempio, si considera il caso di una vendita di scarpe da ginnastica: - Cliente contiene il main, ovvero l’istanza di Commesso e di Scarpe - Creiamo l’interfaccia Scarpe e la facciamo implementare da ScarpeGinnastica e ScarpeTennis - Creiamo Commesso e scriviamo un metodo di tipo Scarpe getScarpe() che riceverà in input un certo valore. In base all’input richiama la sottoclasse CommessoGinnastica o CommessoTennis - CommessoGinnastica e CommessoTennis estenderanno Commesso e faranno l’override del metodo GetScarpe() - Si otterrà come risultato la restituzione di un oggetto ScarpeTennis o ScarpeGinnastica in base all’input Altro esempio di utilizzo è un’applicazione di documenti, sfruttata per l’apertura, la creazione e il salvataggio di essi stessi (il pattern è utilizzato spesso nei framework per creare oggetti in modo modulare e flessibile). Uno dei vantaggi del Factory Method è quello di separare le applicazioni e le famiglie di classi, per cui una modifica richiederebbe un cambiamento minimo nel codice dell’applicazione e inoltre oggetti customizzati possono facilmente rimpiazzare oggetti originali. Di contro, il pattern può essere utilizzato solo su una famiglia di oggetti! BY TARTAGLIA LUCA 🐼 Abstract Factory Il pattern Abstract Factory è un pattern creazionale basato su oggetti che permette di creare oggetti senza conoscerne i dettagli (come il Factory). La motivazione del suo utilizzo è la modularizzazione (conosciuto anche come “Kit”): rende possibile l’aggiunta di nuove funzionalità alle classi esistenti. Terminologia: AbstractFactory è l’interfaccia che espone le operazioni realizzate da Product ConcreteFactory implementa le operazioni per creare gli oggetti dei prodotti AbstractProduct è l’interfaccia che espone le operazioni dei prodotti Product è l’implementazione dei prodotti Client invoca l’interfaccia per creare prodotti Il pattern può essere applicato su: un sistema indipendente dalla creazione, composizione e rappresentazione dei suoi prodotti; un sistema configurato con molte famiglie di prodotti. Lo scopo è creare una libreria di prodotti e conoscere solo le loro interfacce, non l'implementazione. Spesso viene utilizzato con altri pattern come il Singleton per costruire una sola istanza di ConcreteFactory, o con Prototype per semplificare e incrementare le performance. Un esempio di applicabilità è il seguente: - Si possiedono due diversi impianti musicali, uno fisso e l’altro portatile. - Entrambi possono utilizzare diversi supporti, ed hanno in comune CD, USB e nastro. - Quando si va a creare il supporto richiesto, allora si dovrà anche definire l’impianto per il quale viene realizzato. Ma generalmente, il pattern Abstract Factory potrebbe essere applicato per il Look&Feel di un’interfaccia grafica, con una GUI che supporta diversi aspetti grafici che definisce alcuni controlli (come bottoni e campi di testo). BY TARTAGLIA LUCA 🐼 Factory Il Factory Pattern è un derivato del Factory Method e dell’Abstract Factory. Come loro, ha lo scopo di nascondere la logica di istanziazione al client creando oggetti attraverso un’interfaccia. È uno dei pattern più usati nei moderni linguaggi di programmazione a oggetti. Builder Il pattern Builder permette di creare oggetti complessi tramite un processo di costruzione passo dopo passo utilizzando oggetti semplici. Questo pattern separa la rappresentazione di un oggetto dal processo di costruzione, rendendo possibile creare diverse rappresentazioni con lo stesso processo di costruzione. Il processo di costruzione rende indipendenti le parti per costruire e assemblare l’oggetto, e deve permettere diverse rappresentazioni degli oggetti costruiti. Terminologia: Director costruisce il prodotto tramite l’interfaccia builder Product rappresenta il prodotto complesso costruito da Builder Builder è l’interfaccia che implementa le parti del prodotto ConcreteBuilder costruisce, assembla e tiene traccia degli oggetti che costruisce BY TARTAGLIA LUCA 🐼 Un esempio di applicazione potrebbe essere quello di una casa automobilistica (che può costruire auto, biciclette, motociclette e scooter) oppure un’applicazione per gestire gli esami degli studenti che deve fornire un’interfaccia che restituisce informazioni in base al tipo di utente “costruito” (se admin o user). Prototype Il pattern Prototype permette di creare nuovi oggetti copiando un’istanza prototipale, in modo da non conoscere i dettagli o la classe degli oggetti da creare. Questo rende il sistema indipendente dalla creazione, composizione e rappresentazione degli oggetti. Le classi da istanziare possono essere specificate in tempo di esecuzione, evitando di scrivere una gerarchia di classi. Inoltre, è più conveniente copiare un’istanza esistente che crearne una nuova. Il processo di creazione è gestito dinamicamente da un prototype manager, che salva e recupera gli oggetti desiderati in modo da poter essere usati a run-time. Esempio di applicabilità: - Supponiamo di voler accedere a diverse forme (classi concrete Cerchio, Triangolo, Quadrato) che implementano la classe astratta Figura. - Si crea una classe FiguraCache che memorizza gli oggetti in una struttura dati (ad esempio una hashtable) che dovrà restituire cloni quando richiesto. - A questo punto il Main userà FiguraCache per recuperare gli oggetti. BY TARTAGLIA LUCA 🐼 Pattern comportamentali I pattern comportamentali forniscono soluzione alle più comuni interazioni tra oggetti. Rispondono alle domande: in che modo un oggetto svolge la sua funzione? Come collaborano tra di loro gli oggetti? Chain of Responsability (CoR) Il compito del pattern è quello di separare il mittente e il destinatario di una richiesta. La richiesta dovrà essere gestita da una catena gerarchica di oggetti (catena gerarchica di responsabilità). Quando la richiesta viene ricevuta, passa da una lavorazione generale ad una più specializzata, dall’alto verso il basso, per raggiungere un oggetto che possa processarla. Il mittente non conosce chi dovrà gestire la richiesta, ma solo a chi dovrà essere inviata. Terminologia: Handler è l’interfaccia che gestisce le richieste ConcreteHandler gestisce le richieste delle quali è responsabile; può accedere al suo successore e può inviargli la richiesta Client inizia la richiesta di un oggetto concreto della catena Il pattern viene usato principalmente quando l’insieme di oggetti che può gestire una richiesta cambia dinamicamente e quando non si conosce a priori quale oggetto può gestire una determinata richiesta. Un esempio di applicabilità può essere quello di un call-center: il personale che prende la chiamata dovrà indirizzarla verso un indirizzo di secondo livello che proverà a gestirla; se il secondo livello non può gestirla, allora la richiesta sarà inoltrata al terzo livello e così via. Command Il Command pattern incapsula una richiesta in un oggetto per poter parametrizzare i client, gestire richieste diverse, eseguire richieste in momenti diversi e supportare operazioni di undo. Questo pattern è utile quando si vogliono richiedere compiti a oggetti senza conoscere tutte le informazioni sulle operazioni richieste o sul ricevente delle richieste. Questo pattern è utilizzato per parametrizzare oggetti in base alle azioni che devono svolgere, per eseguire richieste in momenti diversi e per supportare azioni annullabili. Inoltre, questo pattern disaccoppia l’oggetto che richiede l’azione dall’oggetto che esegue l’azione. BY TARTAGLIA LUCA 🐼 Il pattern Command separa la chiamata ad un metodo dalla sua implementazione, separando il client dal ricevitore, usando un invocatore. Tra l’invocatore ed il ricevitore si inserisce un’interfaccia Command che si implementerà in tutte le classi che richiedono accesso al ricevitore. Terminologia: Client colui che richiede il comando ed imposta il Receiver Invoker colui che effettua l’invocazione del comando Command interfaccia generica per l’esecuzione del comando ConcreteCommand implementa il comando che collega Invoker e Receiver Receiver colui che, presi i dati di input, esegue il comando Grazie alla sua struttura risoluta, il pattern: Risulta facile da estendere (semplicemente implementando l’interfaccia Command) Riduce l’accoppiamento (evitando di diffondere i dettagli implementativi) Consente di parametrizzare i client con richieste diverse ed è anche conosciuto come “Action” o “Transaction”. Spesso si rende necessario richiedere dei compiti ad oggetti senza conoscere tutto sulle operazioni richieste o il ricevente delle richieste, come ad esempio tool per interfacce utente. Esempio: Il pattern è già usato in java.lang.Runnable e javax.swing.Action. BY TARTAGLIA LUCA 🐼 Interpreter Si usa per definire/valutare la grammatica o l’espressione della lingua. Prevede l’implementazione di un’interfaccia “Espressione” che interpreta il contesto specifico. Le espressioni possono essere: - Terminali, quando in modo autonomo definiscono il loro comportamento (ad esempio un numero definisce il suo simbolo ed il suo comportamento [Integer.parseInt]) - Non terminali, quando dipendono da altre espressioni (ad esempio un’operazione aritmetica definisce il suo simbolo ma il comportamento dipende dal numero) Terminologia: Client, costruisce un albero sintattico, formato da NonTerminalEx e TerminalEx, il quale andrà ad essere elaborato dall’interprete Context, contiene le informazioni che l’interprete dovrà elaborare AbstractExpression, definisce un metodo astratto che verrà definito nelle espressioni (si può usare anche un’interfaccia) TerminalExpression, implementa il comportamento delle espressioni semplici NonTerminalExpression, implementa il comportamento delle espressioni composte richiamando il comportamento di quelle semplici Nota: tramite l’estensione delle classi è facile cambiare grammatica ma è ostico gestire una grammatica complessa! Un esempio di implementazione potrebbe essere il seguente: - Creare un’interfaccia Espressione che implementa il metodo interpreta che lavora sull’oggetto di tipo Contesto. - L’interfaccia viene implementata da Addizione e da Numero, dove: Numero inserisce nel Contesto un numero con il suo simbolo e il suo valore, mentre Addizione estrae i valori dal contesto e li somma, implementando l’operazione addizione. - Contesto definisce uno stack contenente i valori da sommare (di tipo Espressione). - Nella classe Client si dichiarano gli addendi del contesto e si richiama l’operazione di addizione: addizione.interpreta(contesto); BY TARTAGLIA LUCA 🐼 Il pattern Interpreter è già usato in java.util.Patterb e in java.Text.Format. Iterator Il pattern Iterator si usa quando si vuole accedere agli elementi di un aggregato di oggetti senza esporre la loro struttura interna. L’obiettivo è quello di separare l’utilizzatore e l’implementatore mediante un oggetto intermedio che espone lo stesso metodo a prescindere dalla tipologia di oggetto che si vuole restituire. In poche parole, si fornisce un’interfaccia uniforme per lo scorrimento di collezioni differenti. Terminologia: Iterator espone i metodi di accesso alla struttura dati ConcreteIterator implementa Iterator e tiene il puntatore sulla struttura Aggregate definisce l’interfaccia per creare un oggetto Iterator ConcreteAggregate implementa l’interfaccia di creazione di un oggetto Iterato Iterator è usato in JDK con lo Java Collection Framework e java.util.Scanner. BY TARTAGLIA LUCA 🐼 Mediator È un pattern che permette lo scambio di messaggi tra diversi colleghi passando tramite un Mediator. Il pattern Mediator definisce un oggetto che incapsula l’interazione tra oggetti per renderla più facilmente modificabile. Questo è utile in situazioni in cui le interazioni sono complesse e non ben strutturate, rendendo difficile la riutilizzabilità degli oggetti. Inoltre, il pattern Mediator permette di personalizzare il comportamento distribuito senza creare molte sottoclassi. Ne è un esempio un’applicazione per chat. Terminologia: Mediator definisce un’interfaccia per comunicare con i colleghi ConcreteMediator mantiene la lista dei colleghi e implementa lo scambio dei messaggi (send e receive) Colleague definisce l’interfaccia dei colleghi ConcreteColleague implementa il singolo collega e le modalità di comunicazione con il Mediator BY TARTAGLIA LUCA 🐼 Memento Questo modello serve per salvare e ripristinare lo stato interno di un oggetto senza violare la sua incapsulazione. Può essere utile in caso di errori, ad esempio per una calcolatrice che mantiene un registro delle operazioni effettuate. Il modello viene usato quando è necessario catturare lo stato di un oggetto per poterlo ripristinare successivamente. Siccome ottenere informazioni sullo stato precedente di un oggetto viola il principio di incapsulamento, lo stato va salvato in diversa sede. Terminologia: Caretaker istanzia Originator e produce il memento tramite il metodo createMemento() di quest’ultimo Originator memorizza lo stato temporaneo tramite le proprietà interne e memorizza lo stato esterno tramite Memento ConcreteMemento si occupa di mantenere lo stato di Originator, permette solo a quest’ultimo di accedere (siccome si tratta di una classe innestata privata) Memento è l’interfaccia implementata da ConcreteMemento Un esempio di applicazione è, come detto prima, una calcolatrice che ricorda lo stato precedente. Per evitare che il Memento risulti accessibile ad altre classi, lo si dichiara all’interno della classe Originator come classe pubblica ma con attributi e metodi privati. Observer Questo pattern definisce una relazione tra oggetti in cui se un oggetto cambia stato, tutte le sue dipendenze sono automaticamente notificate e aggiornate. Questo è noto anche come Publish-Subscribe. Serve per mantenere la consistenza tra gli oggetti in un sistema composto da molte classi e per evitare un forte accoppiamento tra di loro. Questo pattern è utile quando due aspetti di un’astrazione dipendono l’uno dall’altro e quando il cambiamento di un oggetto richiede il cambiamento di altri, ma non si conosce quanti. Il pattern si usa quando esistono relazioni “uno a molti” tra le classi; viene usato in molte librerie e nel pattern architetturale MVC. BY TARTAGLIA LUCA 🐼 Terminologia: Observable espone un’interfaccia che permette agli osservatori di osservare ConcreteObservable ConcreteObservable mantiene lo stato e notifica agli osservatori una modifica Observer espone l’interfaccia che permette di aggiornare lo stato in caso di modifica del ConcreteObservable ConcreteObserver implementa Observer e ne definisce il comportamento in fase di modifica Un esempio può essere un’applicazione che avvisa il cambio di punteggio in una partita di calcio. C’è una classe ObservableMatch e una Observable, quando il parametro matchScore cambia, attraverso l’apposito setter verrà richiamato il metodo update che notifica a tutti gli Observer il nuovo valore di score. Per essere precisi in setMatchScore (che altera il matchScore di ObservableMatch) si scorre l’elenco degli Observer e si richiama il metodo update. State Lo scopo di questo modello è permettere ad un oggetto di cambiare il proprio comportamento a runtime in base al suo stato interno, sembrando come se cambiasse classe. Si utilizza quando il comportamento di un oggetto dipende solo dalla sua classe e la logica del cambiamento di stato viene implementata in una sola classe invece che con condizionali nella classe che implementa il comportamento. Questo modello evita stati inconsistenti. In pratica, permette di scorporare grossi e complessi blocchi condizionali in oggetti di stato. BY TARTAGLIA LUCA 🐼 Terminologia: Context mantiene un’istanza della concreteState che definisce lo stato corrente State è un’interfaccia che incapsula il comportamento associato ad un particolare stato ConcreteState (State*) sono sub-classi che implementano un comportamento associato ad uno stato Esempio di applicazione: - Consideriamo di avere uno strumento di painting, dove possiamo selezionare un colore per disegnare. La palette potrà assumere il colore Rosso, Giallo o Arancio che ne rappresenterà lo stato. - Palette dovrà avere una referenza ad un oggetto che implementa Colore che varia al variare dello stato. L’interfaccia Colour implementerà il metodo showColor che va ridefinito nelle classi concrete Rosso, Arancione e Giallo. BY TARTAGLIA LUCA 🐼 Strategy Lo scopo del pattern "Strategy" è creare un insieme di algoritmi intercambiabili. Questi algoritmi possono essere modificati separatamente dai client che li utilizzano. Il pattern viene utilizzato quando è necessario modificare dinamicamente il comportamento di un’applicazione, ad esempio scegliere tra diverse opzioni di algoritmi a runtime. Viene utilizzato anche in classi correlate che hanno comportamenti diversi o quando una classe definisce diversi comportamenti. In poche parole, permette di eseguire uno stesso comportamento in modi differenti. Lo si implementa quando si ha necessità di usare diversi algoritmi per ottenere uno stesso risultato. Terminologia: Context detiene informazioni di contesto (dati ed algoritmi) ed ha il compito di invocare l’algoritmo Strategy dichiara un’interfaccia che viene invocata dal Context in base all’algoritmo scelto ConcreteStrategy effettua override del metodo context per ritornare l’implementazione dell’algoritmo Un esempio di applicazione è la creazione di un’interfaccia che permetta di applicare diversi algoritmi di ordinamento ad una struttura dati, in modo tale che chiamando il metodo dell’interfaccia (secondo un dato criterio) venga implementato il miglior algoritmo del caso. Template Method Questo pattern serve a definire la struttura di un algoritmo, lasciando a delle sottoclassi il compito di implementare parti specifiche dell’algoritmo. Questo permette di modificare il comportamento dell’algoritmo senza cambiarne la struttura. È utile quando si vuole evitare di ripetere il codice comune e quando si vuole dare controllo sul comportamento delle sottoclassi rispetto alla superclasse. In poche parole, definisce un algoritmo che permette di implementare i dettagli dei metodi alle sottoclassi. È molto utilizzato, ma viola i principi di programmazione SOLID. BY TARTAGLIA LUCA 🐼 Terminologia: FrameworkClass definisce il metodo concreto ed i metodi primitivi astratti; i metodi primitivi implementati nelle sottoclassi vengono richiamati dal metodo concreto (ApplicationClass). ApplicationClass implementa i metodi primitivi per svolgere i passi specifici dell’algoritmo. Visitor Lo scopo di Visitor è separare un algoritmo dalla struttura di oggetti composti a cui è applicato, permettendo di definire nuove operazioni senza dover modificare le classi degli elementi su cui opera. Viene utilizzato per eseguire operazioni indipendenti e non relazionate tra loro sugli oggetti di una struttura composta, mantenendo la flessibilità di aggiungere nuove operazioni senza modificare la struttura stessa. Questo pattern è utile in situazioni in cui la struttura composta è costituita da molte classi con interfacce diverse e si desidera eseguire un’operazione differente su ogni oggetto in base alla sua classe concreta. In poche parole, il pattern si applica ad una collezione di elementi in una struttura sottoforma di operazione che può essere modificata senza alterare le classi degli elementi dove opera. BY TARTAGLIA LUCA 🐼 Terminologia: Element definisce il metodo accept() che prende un Visitor come argomento. ConcreteElement implementa un oggetto Element che prende un Visitor come argomento. ObjectStructure contiene una collezione di Element che può essere visitata dagli oggetti Visitor. Visitor dichiara un metodo visit() per ogni Element; il nome ed il parametro del metodo identificano l’Element invocante. ConcreteVisitor implementa visit() e definisce l’algoritmo da applicare in base all’Element passato. Un esempio pratico può essere lo shopping al supermercato, dove il carrello è una struttura dati (ObjectStructure). Il cassiere è il Visitor che prendendo gli acquisti, alcuni prezzati ed altri da pesare, ha lo scopo di dire il totale. Pattern strutturali Questi pattern sfruttano le interfacce per riutilizzare oggetti piuttosto che crearne nuovi. Adapter Lo scopo del pattern Adapter è di adattare l’interfaccia di una classe per renderla compatibile con le richieste dei client. Questo pattern viene utilizzato quando si vuole utilizzare una classe esistente che presenta un’interfaccia diversa da quella desiderata o quando si scrive una classe senza conoscere a priori le altre classi con cui dovrà operare. Il pattern Adapter è anche conosciuto come Wrapper (involucro). In poche parole, il pattern Adapter viene usato quando si ha la necessità di rendere compatibili due interfacce che non lo sono. Terminologia: Client rappresenta la classe che richiede un’operazione, contiene un’istanza di Target. Target è l’interfaccia specifica usata dal Client. Adaptee è l’interfaccia da rendere compatibile con Target Adapter è la classe che adatta l’interfaccia dell’Adaptee per soddisfare le richieste del Client. BY TARTAGLIA LUCA 🐼 Il Client interagisce con l’Adapter tramite l’interfaccia Target, mentre l’Adapter converte le richieste del Client in chiamate all’Adaptee. È possibile utilizzare la classe Adapter in due modi: - Usare una composizione, ovvero implementare l’interfaccia Target e fare riferimento ad un oggetto Adaptee per poi implementare i metodi richiesti dal Target. - Usare l’ereditarietà estendendo la classe da adattare in una classe che implementa l’interfaccia Target (ciò significa creare una nuova classe per ogni classe da adattare). Composite Il pattern Composite serve a rappresentare gerarchie composte da oggetti singoli (leaf) e composti (composite). Permette ai client di trattare questi oggetti in modo uniforme, senza dover sapere se si tratta di un leaf o un composite. Viene utilizzato in applicazioni che manipolano una collezione di oggetti primitivi e composti e permette di semplificare il codice per gestirli. La composizione è ricorsiva e l’oggetto utilizzatore costruisce una struttura simil-albero. In poche parole il Composite modifica la struttura di oggetti già esistenti: - Se l’oggetto desiderato è una foglia, la richiesta è processata direttamente - Se l’oggetto è una composite, viene rimandata ai figli per svolgere le operazioni precedenti Terminologia: Client rappresenta la classe che utilizza gli oggetti di una gerarchia Component rappresenta l’interfaccia che viene implementata da tutti i tipi di oggetti nella gerarchia, sia singoli che composti Leaf rappresenta le foglie della gerarchia che non possono essere divise ulteriormente Composite rappresenta gli oggetti composti che possono essere divisi in altri oggetti; in questa tabella vengono definite le operazioni per gestire gli elementi figli. Un esempio di applicabilità è il seguente: - Consideriamo di avere un Client che vuole accedere ad un FileSystem, il quale è formato da File (elemento semplice) e Cartelle (contenitore). BY TARTAGLIA LUCA 🐼 - Bisogna permettere al Client di accedere al Filesystem senza conoscere la natura degli elementi che lo compongono, per poterli trattare tutti allo stesso modo. Per fare ciò si utilizza la stessa interfaccia di accesso mentre l’implementazione nasconderà la gestione dell’oggetto in base alla natura. - Sia la classe File che Cartella implementeranno l’interfaccia Filesystem. Così facendo, è possibile accedere a diversi oggetti utilizzando gli stessi metodi. Bridge Il Bridge Pattern separa un’astrazione (ad esempio, un'idea o un concetto) dalla sua implementazione (ad esempio, come viene effettivamente realizzato). Questo permette a entrambi di essere modificati separatamente, senza che ciò influisca sull’altro. Questo pattern è utile quando è necessario evitare un legame permanente tra l’astrazione e l’implementazione, o quando entrambi hanno bisogno di essere modificati indipendentemente. Usando questo pattern, il codice del client può rimanere intatto senza dover essere ricompilato. Il Bridge Pattern preferisce la composizione all’eredità. Terminologia: Client colui che invoca l’operazione di interesse Abstraction interfaccia del dominio applicativo interessata dal Client AbstractioImp implementazione dell’interfaccia Abstraction Implementor interfaccia di collegamento (Bridge) riferibile agli oggetti concreti da usare ConcreteImplementor implementa Implementor per il transito degli oggetti Un esempio di applicabilità è avere un’interfaccia DisegnoAPI che disegna un cerchio di diversi colori. BY TARTAGLIA LUCA 🐼 Decorator Il pattern Decorator serve a dare nuove funzionalità ad un oggetto esistente in modo dinamico. Invece di estendere le funzionalità tramite ereditarietà, si crea una nuova classe decoratore che "avvolge" l'oggetto originale. Questo pattern è utile quando si vuole aggiungere comportamenti o stati ad oggetti individuali durante il run-time, e l’ereditarietà non è una soluzione flessibile poiché è statica. Terminologia: Interface (Component) rappresenta l’interfaccia per gli oggetti ai quali possono essere aggiunte responsabilità. CoreFunctionality (ConcreteComponent) è l’oggetto a cui possono essere aggiunte responsabilità. OptionalWrapper (Decorator) mantiene un riferimento ad un oggetto da decorare ed implementa l’interfaccia responsabile Optional (ConcreteDecorator) aggiunge responsabilità all’oggetto Component Un esempio pratico è quello di definire un’interfaccia NotebookInterface che contiene quattro casi d’uso per salvare le note di un pc, che viene implementata da Notebook che a sua volta utilizza una lista per salvare le note. Il decorator sarà una classe astratta NotebookDecorator che implementa NotebookInterface. Andrà dunque creata una sola classe per ogni comportamento che si vuole aggiungere; la/le classi in questione dovranno estendere NotebookDecorator. Oltre a migliorare la testabilità e semplificare la gerarchia delle classi, il pattern Decorator incoraggia la scrittura di codice che aderisce ai principi di programmazione solida, ed incoraggia l’uso dell’inversione di dipendenze. BY TARTAGLIA LUCA 🐼 Facade Il pattern Facade serve a semplificare l’interfaccia di un sottosistema complesso, rendendola più facile da gestire per i client. Crea un’interfaccia di alto livello che maschera le interfacce più complesse all’interno del sottosistema. Il pattern è utile quando si vuole fornire un’interfaccia più semplice per lavorare con un sottosistema complesso e separare i client dalla complessità del sottosistema stesso. In questo modo, modificando le classi “dietro la facciata” non è necessario aggiornare i client, mantenendo un’interfaccia standard. Un esempio applicabile è il seguente: - Si considera la necessità di implementare un Webservice per eseguire diversi metodi. - Si preparano le classi contenenti le funzionalità richieste, dopodiché si costruisce la classe Facade, la quale dovrà contenere un metodo che si occuperà di ritornare e gestire la richiesta del Client, controllando l’intero business task. Il discorso fatto per le classi è applicabile anche alle interfacce. Flyweight L’obiettivo di Flyweight è separare la parte variabile di una classe dalla parte che può essere riutilizzata. Questo permette di condividere quest’ultima tra diverse istanze. La motivazione è ridurre i costi di memoria per supportare un gran numero di oggetti con parti di stato in comune. Questo pattern è applicabile quando un’applicazione utilizza molte istanze di una classe e i costi di memorizzazione sono alti. Esso deve essere immutabile, cioè deve: - Evitare la generazione di un gran numero di oggetti - Evitare di creare oggetti che occupano troppa memoria - Semplificare, a livello computazionale, la creazione di oggetti - Migliorare la gestione delle risorse (limitate): memoria, spazio, calcolo. Si definisce dunque: - Stato interno come le caratteristiche dell’oggetto indipendenti dal contesto di utilizzo (immutabili) - Stato esterno come le caratteristiche dipendenti dal contesto di utilizzo, che possono variare senza modificare le caratteristiche dell’oggetto. BY TARTAGLIA LUCA 🐼 Lo stato interno viene condiviso per evitare di generare oggetti duplicati con lo stesso stato. Lo stato esterno rimane variabile e non condiviso, in quanto soggetto a facili cambiamenti. Ad esempio, considerando una tabella: il numero di colonne rappresenta il suo stato interno, mentre il numero delle celle rappresenta il suo stato esterno. Terminologia: Client mantiene una referenza agli oggetti Flyweight; definisce lo stato esterno degli oggetti da creare. Flyweight è l’interfaccia che permette di definire lo stato esterno alle classi concrete. ConcreteFlyweight implementa Flyweight e definisce eventuali stati interni. FlyweightFactory crea e gestisce oggetti Flyweight, permettendo accesso agli oggetti creati, o creandoli nel caso non esistano. E’ usato in JDK su tutte le classi wrapper valueOf(), anche in String Pool in Java String. Proxy Il pattern Proxy serve a fornire un rappresentante (surrogato o placeholder) per un altro oggetto per controllare l’accesso ad esso. Questo è fatto per gestire risorse costose o difficili da duplicare, come connessioni di rete o file di grandi dimensioni. Ci sono diversi tipi di proxy, come Remote Proxy per rappresentare oggetti remoti, Virtual Proxy per creare oggetti costosi solo quando necessario, Protection Proxy per controllare l’accesso all’oggetto originale, e Smart Proxy per eseguire azioni aggiuntive quando si accede all'oggetto. In linea generale, è una classe che funziona come se fosse un’interfaccia di una qualunque cosa. Si crea una singola istanza dell’oggetto e molteplici oggetti Proxy sui quali lavorare. Le operazioni svolte sui Proxy vengono trasmesse all’oggetto originale. Una volta che le istanze del Proxy vengono distrutte, sarà possibile deallocare l’oggetto originale. Esso è utile quando l’oggetto complesso: - Richiede molto tempo per caricarsi - Richiede troppe risorse computazionali - È locato su macchina remota soggetta a limiti di traffico - Permette accesso indiscriminato - Viene rigenerato ad ogni richiesta e non mantenuto in cache È possibile combinarlo con il pattern Flyweight per ridurre lo spazio di memoria occupato dall’oggetto. BY TARTAGLIA LUCA 🐼 Terminologia: Client invoca l’operazione di interesse. Subject è l’interfaccia implementata da Proxy e RealSubject, usata da Client. RealSubject è l’oggetto reale al quale dovrà sostituirsi Proxy. Proxy è la classe che sostituisce l’oggetto reale, al quale dovrà mantenere una reference; dovrà inoltre creare e distruggere l’oggetto ed esporre i metodi richiesti dall’interfaccia. BY TARTAGLIA LUCA 🐼 Model View Controller Il modello MVC (Model View Controller) è un pattern di architettura utilizzato per la costruzione di applicazioni grafiche. Questo modello separa i componenti di un’applicazione software in tre parti distinte: il modello, la vista e il controller. Il modello rappresenta le funzionalità di business dell'applicazione, la vista si occupa della logica di presentazione e il controller utilizza queste funzionalità. La vista e il modello sono relazionati tramite il pattern Observer, ovvero la vista “osserva” il modello. Inoltre, anche il controller “osserva” la vista. Il modello MVC supporta una registrazione dinamica dei componenti al runtime. Inoltre, la strategia utilizzata potrebbe semplificare la possibilità di cambiare la mappatura tra algoritmi che regolano i processi del controller e le interazioni dell’utente con la vista. Le Java Servlet sono un’architettura per lo sviluppo di applicazioni web che seguono il modello MVC. Le servlet possono essere viste come il Controller nell’architettura MVC, che gestisce la logica di business e le interazioni con il modello (spesso implementato come un database o delle classi model) e con la vista (spesso implementata come JSP). La vista utilizza i dati forniti dal modello attraverso il controller per generare la pagina web visualizzata all’utente. Questo schema di separazione delle responsabilità rende più facile la manutenzione e lo sviluppo delle applicazioni web. BY TARTAGLIA LUCA 🐼 Data Access Object Il modello DAO è un pattern di progettazione software utilizzato per separare la logica di accesso ai dati dalla logica del business. Questo pattern prevede l’utilizzo di un’interfaccia che definisce le operazioni standard che un oggetto del modello deve supportare. Ci sono classi di implementazione che implementano questa interfaccia e sono responsabili per l’accesso effettivo ai dati, che possono essere archiviati in un database, in un file XML o in qualsiasi altro meccanismo di archiviazione. L’oggetto modello, noto anche come oggetto di valore, è semplicemente un oggetto POJO (Plain Old Java Object) che include metodi per impostare e recuperare i dati. Questo oggetto modello viene utilizzato insieme alle classi DAO per recuperare i dati dalla fonte. Esempio pratico: Per implementare questo pattern, creeremo un oggetto modello chiamato "Studente", un’interfaccia di accesso ai dati chiamata "StudentDao" e una classe di implementazione chiamata "StudentDaoImpl". La classe "StudentDaoImpl" implementerà l’interfaccia "StudentDao" e sarà responsabile per l’accesso ai dati. Infine, la classe dimostrativa chiamata "DaoPatternDemo" viene utilizzata per dimostrare l’utilizzo del modello DAO. BY TARTAGLIA LUCA 🐼 Gestione dei Thread Il multithreading è un concetto di programmazione che permette di eseguire più compiti in parallelo all’interno di un unico processo. Ciò aumenta la velocità e l’efficienza dell’applicazione. In Java, i thread possono essere creati utilizzando la classe Thread o l’interfaccia Runnable. La classe Thread è utilizzata quando non si hanno altre relazioni da gestire, mentre l’interfaccia Runnable è usata quando si ha bisogno di estendere un’altra classe. Il metodo run() definisce il comportamento del thread e l’avvio di un thread viene effettuato tramite la chiamata del metodo start(). E’ possibile forzare la fine tramite il metodo stop(). Nelle situazioni in cui più thread hanno bisogno di accedere contemporaneamente a una fonte di dati condivisa, è necessario sincronizzare i loro metodi (thread synchronized). Questo si ottiene tramite il concetto di lock (o monitor). Ogni oggetto ha un lock associato e un thread deve acquisire il lock per accedere all’oggetto e rilasciarlo al termine dell’operazione. Esiste una varietà di livelli di sincronizzazione, basati su metodi, blocchi, variabili o static. synchronized (nomeOggetto) { … } Esempio public class MyThread implements Runnable { public void run() { System.out.println("Eseguo il comportamento del thread"); } } public class Main { public static void main(String[] args) { MyThread mt = new MyThread(); Thread t = new Thread(mt); t.start(); } }