Showing posts with label Design(Italiano). Show all posts
Showing posts with label Design(Italiano). Show all posts

Il Pattern "Observer" attraverso un Esempio

Il Pattern "Observer" attraverso un Esempio
G.Morreale

Introduzione:

L'obiettivo di questo articolo è quello di avvicinarsi al design pattern "observer" cercando di scrivere meno teoria a favore di un esempio concreto circa l'uso del pattern.

Ovviamente è d'obbligo una piccola introduzione teorica prima di procedere con l'esempio.

Observer è un design pattern utilizzato quando un oggetto vuole notificare i suoi cambiamenti di stato ad un gruppo di oggetti che dipendono da lui.

Quindi in tale pattern esiste

  • Un oggetto che cambia stato
  • E degli oggetti che vengono notificati

Il primo viene chiamato "subject" o "publisher", infatti è il soggetto del pattern, colui che pubblica i cambiamenti, 
I secondi invece vengono indicati con il nome di "observer","subscriber" o "listener"; sono gli oggetti che osservano i cambiamenti, sottoscrivono una sorta di contratto con il publisher al fine di essere avvisati sui suoi cambiamenti o se vogliamo sono ascoltatori di nuove notifiche.

Come è già facile intuire in questo pattern si crea una relazione uno a molti tra soggetto e listeners.
Tale relazione viene stabilita a run-time.

Nel caso specifico in cui l'observer è uno solo si usa più frequentemente il nome di callback piuttosto che observer.


Esempio

Supponiamo di voler realizzare una piccola applicazione in grado di notificare l'utente attraverso l'apertura di un popup quando arriva una nuova mail sulla sua casella di posta elettronica.


Esisterà una classe che rappresenta il Subject, ovvero il gestore di posta, che in relazione all'arrivo di una nuova mail (cambio di stato) notifica un oggetto grafico (observer) al fine di avvisare l'utente sull'arrivo di una nuova mail.

Cosa è necessario per far funzionare l'esempio ?

Una versione aggiornata di JDK
Le librerie Java MAIL
Un account di posta elettronica in grado di funzionare con il protocollo pop3(o altri supportati da javamail)

nota:
La libreria java mail deve essere nel classpath, nel caso di netbeans è possibile aggiungere la libreria cliccando con il tasto destro sul progetto e accedendo tra le proprietà alla sezione library.

Si consideri di avere due oggetti, uno che si occupa di ricevere la posta, ad ogni arrivo di nuove mail il suo stato interno cambierà; e uno che si occupa di mostrare graficamente il cambio di tale stato.
Al di là del pattern si realizzano in seguito la classe per la ricezione della mail e la classe per la visualizzazione destinata all'utente.

La ricezione della mail

1) Creare un nuovo progetto Java SE
2) Importare le librerie Java Mail
3) Effettuare il copia e incolla della classe MailManager

package esempio;

import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;

/**
 *
 * @author Giuseppe M.
 */
class MailManager implements Runnable
{
    //numero di millisecondi tra un check della cartella inbox ed un altro
    private long interval;    //numero corrente di mail all'interno della cartella. 
    private int total;
    private boolean alive = true; //determina se il thread deve continuare a lavorare o meno.
    private Folder folder; //contiene il riferimento alla cartella INBOX della casella di posta.
    private Store store;//contiene riferimento alla connessione imap

    public MailManager()
    {
        this(2000);
    }

    public MailManager(long millis)
    {        
        interval = millis; //di default ogni due secondi
        initConnection(); //init dell connessione.
        this.total = check();
    }

    /**
     * Inizializza la connessione verso la casella di posta
     */
    private void initConnection()
    {
        try
        {

            String host = "imap.gmail.com";
            String username = "la vostra username";
            String password = "la vostra pwd,.";
            String protocol = "imaps"; //o pop3s
            int count = -1;


            //Otteniamo un oggetto session
            Session session = Session.getInstance(new Properties(), null);

            //Otteniamo lo store
            store = session.getStore(protocol);
            store.connect(host, username, password);

            //Otteniamo la cartella INBOX            
            folder = store.getFolder("INBOX");
            folder.open(Folder.READ_ONLY);
        } catch (MessagingException ex)
        {
            Logger.getLogger(MailManager.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    /**
     * Calcola il numero di mail ogni "interval" millisecondi
     * @return
     */
    public int check()
    {
        // Contiamo i messaggi presenti nella cartella
        int count = -1;
        try
        {
            count = this.folder.getMessageCount();
        } catch (MessagingException ex)
        {
            Logger.getLogger(MailManager.class.getName()).log(Level.SEVERE, null, ex);
        }
        return count;
    }

    private void setIntervals(long millis)
    {
        this.interval = millis;
    }

    public void stop()
    {
        this.alive = false;
        
        try
        {//diamo tempo di effettuare l'ultima lettura prima di chiudere le connessioni
            Thread.sleep(interval + 100);
            folder.close(false);
            store.close();
        } catch (InterruptedException ex)
        {
            Logger.getLogger(MailManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (MessagingException ex)
        {
            Logger.getLogger(MailManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void run()
    {
        //effettua il calcolo ogni interval millisecondi alla ricerca di nuove mail
        //determina l'arrivo di nuove mail calcolando la differenza con il valore 
        //calcolato al passo precedente
        while (alive)
        {
            int newTot = check();

            if ((newTot - this.total) > 0)
            {
                System.out.println((newTot - this.total) + " Nuove Mail");
                this.total = newTot;
            }

            try
            {
                Thread.sleep(interval);
            } catch (InterruptedException ex)
            {
                Logger.getLogger(MailManager.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

4) Incollare il seguente codice nel metodo main

    public static void main(String[] args) 
    {
        MailManager pop3 = new MailManager();
        Thread mailThread = new Thread(pop3);
        mailThread.run();
    }

In tal modo a prescindere dal risultato finale dell'esempio, è possibile testare la comunicazione con la propria casella di posta.
Provando a ricevere una nuova mail (ad esempio auto-inviandosela) noterete nello standard output una nuova riga per ogni mail arrivata:

>> 1 Nuove Mail
>> 2 Nuove Mail
..

Il Popup

Si realizzi adesso un componente grafico in grado di comparire in basso a sinistra dello schermo e mostrare un testo di notifica.
Il codice da utilizzare è il seguente:

public class PopUp extends JDialog
{
    private JLabel label; //etichetta sulla quale scrivere il testo
    
    PopUp()
    {
        label = new JLabel("NO MAIL");
        label.setAlignmentX(JLabel.CENTER);
        label.setAlignmentY(JLabel.CENTER);
        label.setBackground(Color.BLACK);
                
        JPanel panel = new JPanel();
        panel.add(label);
        
        panel.setBackground(Color.WHITE);
        this.add(panel);
        
        //otteniamo le dimensioni dello schermo
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();        
        this.setSize(100, 100);       
        this.setLocation( 3,(int)screenSize.getHeight() - this.getHeight() - 35);        
        this.setResizable(false);
        this.setDefaultCloseOperation(PopUp.DISPOSE_ON_CLOSE);
        this.repaint();
    }
}

Per provare le funzionalità del popup aggiungere nel metodo main le seguenti righe di codice:

 PopUp pop = new PopUp();        
 pop.setVisible(true);

L'implementazione del pattern

Adesso è il momento di miscelare il tutto, rispettando i ruoli di ciascun elemento del design pattern.

Si proceda creando la classe astratta "soggetto" e l'interfaccia al fine di raggiungere lo scopo attraverso l'utilizzo del design pattern observer.

La classe soggetto : Subject, viene implementata come classe astratta in modo da unificare l'implementazione dei metodi addListener e removeListener.
E' possibile comunque definirla attraverso un interface e delegare l'implementazione di tutti i metodi alla classe concreta.


nota:
I nomi dei metodi addListener e removeListener sono analoghi a attach, detach o addObserver.
Allo stesso modo la classe Observer è chiamata Listener.

public abstract class Subject
{

    private MailListener listener;

    public void addListener(MailListener l)
    {
        listener = l;
    }

    public void removeListener()
    {
        this.listener = null;
    }

    public abstract void mailNotify();
}

L'interfaccia MailListener, ovvero l'Observer, banalmente sarà implementata con il seguente codice java:

public interface MailListener {

    public void update();
}

Adesso è necessario procedere con l'implementazione delle classi concrete.

La classe MailManager sarà classe concreta del Subject, quindi responsabile dell'implementazione del metodo notify:


    @Override
    public void mailNotify()
    {
        if (this.listeners != null)
        {
            for (MailListener l : this.listeners)
            {
                l.update();
            }
        }
    }


La classe PopUp, invece, necessita di essere notificata circa i cambiamenti di stato del subject quindi dovrà implementare l'interfaccia mailListener al fine di comportarsi di conseguenza.

public class PopUp extends JDialog implements MailListener
{
    private int newMailCount;
    ..
    public PopUp()
    {
        newMailCount = 0;
        ..
        ..
    }    
    public void update()
    {
        label.setText(++newMailCount + " Nuove Mail Nella casella di posta");
        this.setVisible(true);        
    }     
}

Conclusione

Il pattern observer è stato implementato attraverso un esempio concreto e credo interessante.
Nel particolare caso la relazione instaurata tra il subject e l'observer è uno a uno, quindi si è creata una situazione chiamata callback.

L'esempio può comunque essere esteso a più observer, giusto per fare un esempio si potrebbe creare un altro observer che implementa MailListener al fine di inviare un sms(tramite apposito provider) ad un utente per avvisarlo sulla presenza di nuove mail nella casella di posta elettronica.

Cenni sulla Dependency Injection

Dependency Injection
G.Morreale

Introduzione:

La Dependency Injection è un pattern di programmazione, nello specifico è una particolare forma del pattern Inversion Of Control.
Con la Dependency Injection una classe o un sistema non è responsabile circa l'inizializzazione delle proprie dipendenze.

Cercherò prima di fare un pò di chiarezza sui termini.

Cos'è una "Dependency"?.. Banalmente è un modo diverso di chiamare le variabili di un classe, i suoi campi, o se si preferisce la variabili di istanza.

public class Esempio
{
private Risorsa risorsa;

public void Esempio()
{}

public void foo()
{
..
risorsa.faiQualcosa();
..
}
}


risorsa è una Dependency.

Cos'è "Injection"?..Non è altro che l'inizializzazione della variabile o se si vuole della Dependency!

Per realizzare una Dependency Injection attraverso il costruttore possiamo modificare l'esempio nel seguente modo:

public class Esempio
{
private Risorsa risorsa;

public void Esempio(Risorsa r)
{risorsa = r;}

public void foo()
{
..
risorsa.faiQualcosa();
..
}
}


L'injection avviene quando all'esterno della classe che contiene la Dependency (la variabile di istanza da inizializzare) si passa la dependency stessa già inizializzata.

public class EsempioTest
{
public void Test()
{
Risorsa r = new Risorsa("bla bla");
Esempio esempio = new Esempio(r);
esempio .foo();
}
}


In questo caso la Dependecy Injection è avvenuta tramite costruttore, ma tale tecnica può essere realizzata con un metodo "set" o tramite l'interfaccia.

Esistono infatti 3 tipi di Depency Injection:

  • Constructor Injection
  • Setter Injection
  • Interface Injection

A cosa serve la Dependency Injection?

L'obiettivo di tale tecnica è quello di allentare l'associazione(coupling) tra un oggetto e ed un altro.
Per approfondire circa l'utilità del pattern è consigliabile la lettura di questo articolo http://docs.google.com/View?docid=dg4p5693_22h76wp

La riduzione della dipendenza tra una classe e l'altra, inoltre, facilita le fasi di unit testing rendendo tale pattern adatto per progetti basati sul Test Driven Development(http://www.javaportal.it/rw/43650/editorial.html )

Dependency Injection e Java EE

Nella versione J2EE 1.4 si utilizzava la tecnica del JNDI lookup, la classe che intendeva usare una risorsa/servizio doveva dichiarare esplicitamente tale dipendenza e ottenerla attraverso il JNDI lookup.

Context ic = new InitialContext();
HelloWorld helloWorld = (HelloWorld)ic.lookup("java:comp/env/ejb/HelloWorld");

Con le nuove specifiche è il container che si occupa di effettuare tale lookup e inizializzazione per conto della classe che intende usare tale dipendenza. La classe deve solo indicare che intende usare la dipendenza:

@EJB
private HelloWord hello;


altri esempi

@PersistenceContext
private EntityManager em;

@WebServiceRef
private SmsWebService service;

Le annotation (@EJB, @PersistenceContext etc.) supportano dei parametri che consentono di indicare al container il nome della risorsa, la tipologia e altre opzioni. Ad esempio nel caso del PersistenceContext, nel caso in cui esistono all'interno del modulo ejb due diversi persistence unit è necessario indicare il nome del persistence unit da utilizzare.

@PersistenceContext(unitName="p1")
private EntityManager em1;

@PersistenceContext(unitName="p2")
private EntityManager em2;

Visto che il responsabile di tale operazione di "Injection" è il container il pattern può essere usato solo per le classi managed, quelle unmanaged useranno,ad esempio, la tecnica del JNDI lookup.

Di seguito la lista delle managed class divise per tipologia di container(Web/EJB)

Web (Tipi di risorsa validi per l'injection: DataSource, JMS, Mail, EJB, Environment entries, EntityManager, UserTransaction)
  • Servlet,
  • listener classes,
  • web services end-point,
  • JAX-RPC handlers

EJB (Tipi di risorsa validi per l'injection:DataSource, JMS, Mail, Environment entries, EntityManager, EJB Context, UserTransaction, TimerService)
  • Bean,
  • interceptors,
  • web services end-point

Conclusione

Conoscere la Dependency Injection quindi serve a capire quali meccanismi stanno dietro l'inizializzazione invisibile di quei particolari campi preceduti dall'annotation(@EJB, @TimerService etc.)

Inoltre è possibile in fase di design dei propri progetti implementare tale pattern qualora si necessario disaccoppiare le componenti del progetto.

Il design pattern MVC in Java EE

MVC - Java EE

Introduzione

MVC è un design pattern(una soluzione progettuale generale ad un problema ricorrente) molto utilizzato nella progettazione del software.
MVC Stà per Model Controller View
.
MVC quindi è realizzato attraverso 3 componenti:

  • il model contiene i dati e fornisce i metodi per accedervi;
  • il view visualizza i dati contenuti nel model;
  • il controller riceve i comandi dell'utente (in genere attraverso il view) e li attua modificando lo stato degli altri due componenti

Questo pattern garantisce la suddivisione tra logica di business(gestita dal model) e interfaccia utente(gestia da view e controller).

Volendo applicare tale pattern alla tecnologia Java EE è possibile utlizzare le seguenti tecnologie java applicandole ai vari componenti del pattern.

Model: Tale componente può essere implementato attraverso gli entity bean, e i session bean
Controller: Esso può essere implementato mediante delle servlet.
View: Quest'ultimo attraverso jsp e/o jsf.

Gli entity bean quindi saranno un astrazione per rappresentare i dati, i session bean potranno effettuare delle operazioni sugli entity,
le servlet raccoglieranno l'input dalle jsp (utente) per effettuare richieste ai session bean e comunicare i risultati alle jsp e così via dicendo.

Esempio:

Supponiamo di dover realizzare un'applicazione web che permettare di visualizzare/modificare i dati di una tabella "utenti" presente in un db.

La tabella:Utenti
Le colonne: id, nome, cognome

Inseriamo all'interno della tabella una riga in modo da avere di default almeno un utente.

Supponiamo di utilizzare un persistence provider, ad esempio hibernate
Per tale entità(tabella utenti) è possibile creare un entity bean.
Creaimo inoltre un session bean dotato di entitymanager che realizza i metodi "edit" e "find".

  • edit - dato un entity di tipo utenti effettuera l'update rispetto al database usando il metodo merge dell'entitymanager.
  • find - dato un id, restituisce l'entity bean che rappresenta i dati in tabella che si trovano nella riga con id indicato.
  • .. è possibile implementare anche i metodi "create", "erase" per completezza.

Implementando questa porzione di progetto, si ottiene il componente "Model"

Implementiamo una servlet, (la servlet utente) che richiama mediante la resource injection, @EJB, il session bean.
Quindi dalla servlet richiamando il model e usando il metodo find(1) è possibile ottenere il corrispettivo entity contente i dati dell'utente con id = 1;

Se invece la servlet riceve i parametri GET o POST "id", "nome" e "cognome" allora effettuerà l'aggiornamento sul db, creando un nuovo entity con i dati passati tramite parametri http e richiamando il metodo edit del session bean.
(Nota che quando gli entity abbandonano le componenti ejb, ovvero vengo usati ad esempio, da componenti web, entrano nello stato un-managed..)

La servlet in questo caso realizza il controller.

La servlet in questione setta nell'oggetto request l'attributo "utenti" settando l'istanza utente ottenuta dal session bean e usando un RequestDispatcher effettua un forward verso una jsp che si occupa di mostrare una form html tale da permettere la lettura e la modifica dei dati dell'utente.
La modifica avverà richiamando la servlet utenti di partenza passando opportunamente i rispettivi valori.

Questo breve articolo non ha la presunzione di essere esaustivo, in quanto entrano in gioco diversi concetti e tecnologie.. può comunque costituire punto di partenze per un approccio a MVC  a Java EE.