Etichette

Visualizzazione post con etichetta software design. Mostra tutti i post
Visualizzazione post con etichetta software design. Mostra tutti i post

venerdì 28 novembre 2008

Chain-Of-Responsability Design Pattern

CHAIN-OF-RESPONSABILITY DESIGN PATTERN:

Obiettivo: fare in modo che una richiesta possa essere processata da uno di più oggetti. Tale richiesta passa per una catena di oggetti finchè uno di essi non la gestisce.

Il può noto esempio di applicazione di questo design pattern è la gestione delle eccezioni in java. Quando durante l'esecuzione del metodo di una classe scatta un'eccezione, la classe può gestirela (tramite il meccanismo try-catch) o delegarne la responsabilità della gestione alla classe che ha invocato il metodo(tramite il costrutto throws). Quest'ultima classe può gestirla o a sua volta sollevare l'eccezione alla classe da cui è stata invocata. Un altro esempio di utilizzo di questo design pattern è quello dell'utilizzo della classe javax.servlet.Filter in un'applicazione web. Il metodo chain.doFilter() viene richiamato per fare in modo che tutti i filtri di una servlet possano processare una richiesta prima che essa venga processata dalla stessa servlet. Se un solo filtro della catena blocca la richiesta allora la richiesta viene 'annullata' dal web container.

Nel nostro esempio useremo il design pattern Chain of Responsability per creare una semplice applicazione (a scopo puramente didattico) che determina se una data nel formato ("yyyy" "mm" "dd") è corretta. I requisiti di correttezza sono che
  • "yyyy" "mm" "dd" rappresentino interi,
  • 1<=mm>=12
  • 1<=dd>=31, 1<=dd>=30  per i mesi aprile, giugno, settembre e novembre, 1<=dd>=29 se mm=02 negli anni bisestili, 1<=dd>=28 se mm=02 negli anni non bisestili

Nel seguente listato tali controlli vengono affidati ad una catena di oggetti, ognuno dei quali verifica che sia verificato un requisito specifico. Se uno solo di tali oggetti rivela che la data non è correttamente definita è inutile continuare nei controlli.


package designPatterns.chainofresponsability;

public class Main {
    public static void main(String[] args) {
        MyDate myDate = new MyDate("1981", "13", "6");
        IsNumericFilter isNumericFilter = new IsNumericFilter();
        MonthFilter monthFilter = new MonthFilter();
        DayFilter dayFilter = new DayFilter();
        isNumericFilter.doChain(monthFilter);
        monthFilter.doChain(dayFilter); 
        isNumericFilter.doFilter(myDate);
    }
}

class MyDate{
    private String year;
    private String month;
    private String day;
    public MyDate(String y, String m, String d) {
        this.year = y;
        this.month = m;
        this.day = d;
    }
    public String getDay() {
        return day;
    }
    public String getMonth() {
        return month;
    }
    public String getYear() {
        return year;
    }
}

abstract class MyDateFilter{
    protected MyDateFilter successorMyDateFilter;
    protected String wrongDate = "Data non corretta";
    protected String okDate = "Data corretta!!";
    public void doChain(MyDateFilter successor){
        this.successorMyDateFilter=successor;
    }
    public abstract void doFilter(MyDate myDate);
}

class IsNumericFilter extends MyDateFilter{
    @Override
    public void doFilter(MyDate myDate) {
        if(!isNumeric(myDate.getYear()) ||
                !isNumeric(myDate.getMonth()) ||   
                !isNumeric(myDate.getDay()))
            System.out.println(wrongDate);
        else
            successorMyDateFilter.doFilter(myDate);
       
    }
    private boolean isNumeric(String param){
        try{
            Integer.parseInt(param);
        }catch(NumberFormatException e){
            return false;
        }
        return true;
    }
}

class MonthFilter extends MyDateFilter{
    @Override
    public void doFilter(MyDate myDate) {
        String mString = myDate.getMonth();
        int month = Integer.parseInt(mString);
        if(month<1 || month>12)
            System.out.println(wrongDate);
        else
            successorMyDateFilter.doFilter(myDate);
    }
}

class DayFilter extends MyDateFilter{
    @Override
    public void doFilter(MyDate myDate) {
        String dString = myDate.getDay();
        String mString = myDate.getMonth();
        String yString = myDate.getYear();
        int year = Integer.parseInt(yString);
        int day = Integer.parseInt(dString);
        if(day<1 || day>31)
            System.out.println(wrongDate);
        else if(isShortMonth(mString) && dString.equals("31"))           
            System.out.println(wrongDate);
        else if(mString.equals("2")){
            if(new java.util.GregorianCalendar().isLeapYear(year)){
                if(day>29)
                     System.out.println(wrongDate);
            }else if(day>28)
                 System.out.println(wrongDate);
        }
          
        else
            System.out.println(okDate);
    }
    private boolean isShortMonth(String mString){
        int month = Integer.parseInt(mString);
        if(month==4 || month==6 || month==9 || month==11)
            return true;
        return false;
    }
}


Iterator Design Pattern

ITERATOR DESIGN PATTERN:
Obiettivo: fornire un meccanismo che permetta di 'esplorare' una lista di collection o oggetti aggregati senza conoscerne la rappresentazione interna.

Nel nostro esempio useremo il pattern Iterator per stampare sullo standard output una lista di stringhe.

package designPatterns.iterator;
interface StringListIterator {
    public void first();
    public void next();
    public boolean isDone();
    public String currentString();
}

class StringList {
    private String[] strings;
    private int currIndex;
    private int size;
    public StringList(int size) {
        strings = new String[size];
        currIndex = 0;
        this.size = size;
    }
    public int count() {
        return currIndex;
    }
    public void append(String titleIn) {
        if (currIndex >= size) {
            String[] tempArray = new String[size];
            for (int i = 0; i < size; i++) {
                tempArray[i] = strings[i];
            }
            strings = null;
            size = size + 1;
            strings = new String[size];
            for (int i = 0; i < size - 1; i++) {
                strings[i] = tempArray[i];
            }
        }
        strings[currIndex++] = titleIn;
    }
    public void delete(String titleIn) {
        boolean found = false;
        for (int i = 0; i < (currIndex - 1); i++) {
            if (found == false) {
                if (strings[i].equals(titleIn)) {
                    found = true;
                    strings[i] = strings[i + 1];
                }
            } else {
                if (i < (currIndex - 1)) {
                    strings[i] = strings[i + 1];
                } else {
                    strings[i] = null;
                }
            }
        }
        if (found == true) {
            --currIndex;
        }
    }
    public StringListIterator createIterator() {
        return new InnerIterator();
    }
    private class InnerIterator
            implements StringListIterator {

        private int pos = 0;

        private InnerIterator() {
        }
        public void first() {
            pos = 0;
        }
        public void next() {
            if (pos < (currIndex)) {
                ++pos;
            }
        }
        public boolean isDone() {
            if (pos >= (currIndex)) {
                return true;
            } else {
                return false;
            }
        }
        public String currentString() {
            return strings[pos];
        }
    }
}

public class Main {
    public static void main(String[] args) {
        StringList strings = new StringList(3);
        strings.append("string1");
        strings.append("string2");
        strings.append("string3");
        StringListIterator iterator =
                strings.createIterator();
        while (!iterator.isDone()) {
            String curr = iterator.currentString();
            System.out.println(curr);
            iterator.next();
        }
        strings.delete("string1");
        System.out.println(" ");
        iterator.first();
        while (!iterator.isDone()) {
            String curr = iterator.currentString();
            System.out.println(curr);
            iterator.next();
        }
    }
}

Le API standard di Java forniscono un meccanismo built-in che facilita l'uso del pattern Iterator. Ogni oggetto che implementa java.util.AbstractList (es.: Vector e ArrayList tanto per fare i nomi di due classi ampiamente usate nella programmazione java), esporta il metodo public java.util.Iterator iterator() che corrisponde, per funzionalità, al metodo public StringListIterator createIterator() della classe StringList dell'esempio appena visto. Facendo uso dell'interfaccia java.util.Iterator avremmo ottenuto il seguente listato:


import java.util.*;
public class Main {


Si noti che, essendo Iterator una classe di uso generico (applicabile ad aggregati di Oggetti di qualsiasi tipo), è necessario effettuare il cast a String dell'Object ritornato dal metodo iterator.next(). Nel nostro esempio il cast non è necessario perchè abbiamo definito un Iterator ad hoc il cui metodo currentString() ritorna una Stringa.


Observer Design Pattern

OBSERVER DESIGN PATTERN:
Obiettivo: modificare lo stato di un oggetto in conseguenza del cambiamento dello stato di un altro oggetto.

Questo pattern è usato soprattutto nelle applicazioni GUI, definendo per esempio una relazione uno-a-uno o uno-a-molti tra oggetti swing tale che quando cambia lo stato di uno, lo stato di uno o piu altri cambia in modo automatico. Nel nostro esempio useremo il Pattern Observer (noto anche come Publish-Subscribe) nella creazione di un'applicazione GUI in cui è definito un JFrame (Observer) che visualizza le stringe inserite dall'utente mediante standard input (Observable). La prima classe implementa Observer, la seconda estende Observable, due classi presenti nelle API standard in java.util.


package
designPatterns.observer;

import java.io.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;

public class Main {

    public static void main(String args[]) {
        InputConsole input = new InputConsole();
        OutputForm output = new OutputForm();
        input.addObserver(output);
        input.read();
    }
}

class OutputForm extends JFrame implements Observer {

    JTextField display;

    public OutputForm() {
        display = new JTextField(10);
        getContentPane().add(display);
        setTitle("Observer form");
        setSize(200, 100);
        setLocation(200, 100);
        setVisible(true);
        //exit when closing application
        addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }

    public void update(Observable ob, Object o) {
        if (o instanceof String) {
            String text = (String) o;
            display.setText(text);
        }
    }
}

class InputConsole extends Observable {

    public void read() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            String inputString;
            while (!(inputString = br.readLine()).equals("exit")) {
                setChanged();
                notifyObservers(inputString);

            }
            System.exit(0);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Adapter Design Pattern

ADAPTER DESIGN PATTERN:
Obiettivo: convertire una interfaccia esistente in una nuova interfaccia per ottenere compatibilità e riusabilità di classi non correlate in un' applicazione.

Nelle API java standard le classi che implementano il pattern Adapter sono facilmente riconoscibili (WindowAdapter, ComponentAdapter, MouseAdapter,...) . Come noto l'interfaccia WindowListener ha sette metodi:

public interface Windowlistener {
public void windowClosed(WindowEvent e);
public void windowOpened(WindowEvent e);
public void windowIconified(WindowEvent e);
public void windowDeiconified(WindowEvent e);
public void windowActivated(WindowEvent e);
public void windowDeactivated(WindowEvent e);
public void windowClosing(WindowEvent e);
}

Qualsiasi classe che implementa WindowListener dovrebbe necessariamente definire il contenuto di tutti questi metodi. La classe WindowAdapter implementa questa interfaccia con metodi tutti vuoti:

class WindowAdapter implements WindowListener{
    public void windowClosed(WindowEvent e){}
    public void windowOpened(WindowEvent e){}
    public void windowIconified(WindowEvent e){}
    public void windowDeiconified(WindowEvent e){}
    public void windowActivated(WindowEvent e){}
    public void windowDeactivated(WindowEvent e){}
    public void windowClosing(WindowEvent e){}
}

In questo modo in un'applicazione GUI, per esempio, una classe che ha bisogno di intercettare solo l'evento chiusura della finestra in cui è inserito un JFrame non deve implementare tutti i metodi presenti in WindowListener, ma solo il metodo public void windowClosed(WindowEvent e){} tramite la tecnica nota come Overriding.

class WindowClosingListener extends JFrame {
   public WindowClosingListener () {
        setSize(200, 200);
        setVisible(true);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.out.println("Closing event caught");
                System.exit(0);
            }
        });
    }
    public static void main(String[] args) {
        new WindowClosingListener();
    }
}

Un'altra applicazione di questo pattern (noto anche come Wrapper) è il riuso di classi e creazione di nuove classi compatibili con quelle già esistenti. Usando la composizione, per esempio, si potrebbe 'adattare' una classe già implementata e ben testata ad un nuovo sistema. La classe Adapter dovrà dichiarare tale classe gia esistente come variabile, tramite il meccanismo della composizione, e 'wrapparla' o 'fonderla' in se stessa.

import java.util.Date;
public class DataAdapter {
    private Date date;
    public DataAdapter() {
        date = new Date();
    }
    public DataAdapter(long date) {
        this.date = new Date(date);
    }
    public void setMillisecondi(long time) {
        date.setTime(time);
    }
    public long getMillisecondi() {
        return date.getTime();
    }
    public int confronta(DataAdapter anotherDate) {
        return date.compareTo(new Date(anotherDate.getMillisecondi()));
    }
    public boolean prima(DataAdapter when) {
        return date.before(new Date(when.getMillisecondi()));
    }
    public boolean dopo(DataAdapter when) {
        return date.after(new Date(when.getMillisecondi()));
    }
    @Override
    public String toString() {
        return date.toString();
    }
}


La classe DataAdapter può essere usata nel seguente modo:

    public static void main(String args[]) {
        DataAdapter data1 = new DataAdapter(1225446691789l);
        DataAdapter data2 = new DataAdapter(1225445322893l);
        if (data1.confronta(data2) == 0) {
            System.out.println("Le date coincidono");
        } else if (data1.confronta(data2) > 0) {
            System.out.println(data1 + " > " + data2);
        } else {
            System.out.println(data1 + " < " + data2);
        }
    }

Singleton Design Pattern

SINGLETON DESIGN PATTERN:
Obiettivo: avere una sola istanza di una classe o un unico valore accessibile e modificabile in tutta l'applicazione.

Questo pattern può essere usato per esempio quando si vuole avere all'interno di un'applicazione un unica classe che gestisca tutte le interazioni con il file system, un unico spooler di stampa, un unico Logger, un'unica connessione ad un DB, un unico socket di Input/Output, etc... Nel nostro esempio useremo il Pattern Singleton per avere un unica connessione ad un DB MySQL, ciò garantirà evidentemente un incremento nelle performance dell'applicazione in quanto la creazione e distruzione di una connessione ad un DB sono notoriamente operazioni dispendiose.

Per avere una classe che rispetti il pattern Singleton è necessario definire le sue variabili o metodi pubblici e/o statici, e firmare tutti i costruttori privati, per impedire alla JVM di crearne un'istanza di default. Un noto esempio di classe Singleton è java.lang.Math, la quale non estensibile.

package designPatterns.singleton;
import java.sql.*;

public class Main {
public static void main(String[] args) throws Exception {
DatabaseReader reader = new DatabaseReader("select ...");
reader.request();
DatabaseWriter writer = new DatabaseWriter("update ...");
writer.request();
MyDBConnector.getConnector().close();
}
}

class DatabaseReader{
String request;
public DatabaseReader(String request) {
this.request = request;
}
public String request() throws Exception{
String ret = "";
MyDBConnector connector = MyDBConnector.getConnector();
Connection conn = connector.getConnention();
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(request);
//build ret according to rs
return ret;
}
}

class DatabaseWriter{
String request;
public DatabaseWriter(String request) {
this.request = request;
}
public void request() throws Exception{
MyDBConnector connector = MyDBConnector.getConnector();
Connection conn = connector.getConnention();
conn.createStatement().executeUpdate(request);
}
}

class MyDBConnector{
private static MyDBConnector instance;
private Connection conn;

private static final String url = "jdbc:mysql://...";
private static final String usr = "...";
private static final String pwd = "...";
private MyDBConnector() throws Exception{
Class.forName("com.mysql.jdbc.Driver");
conn = (Connection) DriverManager.getConnection(url,usr,pwd);
}
public static MyDBConnector getConnector() throws Exception {
if(instance==null)
instance = new MyDBConnector();
return instance;
}
public Connection getConnention(){
return conn;
}
public void close(){
if(conn!=null)
try{
conn.close();
}catch(Exception e){}
instance = null;
}
}

Nel nostro caso la classe Singleton è MyDBConnector. Tale classe è non Thread-Safe infatti nel metodo public static MyDBConnector getConnector() throws Exception due o piu thread potrebbero eseguire l'istruzione instance = new MyDBConnector();. Per aggirare questo problema è buona prassi utilizzare la calusola synchronized:

public static synchronized MyDBConnector getConnector() throws Exception {...}

tuttavia questa soluzione potrebbe decrementare le performance dell'esecuzione dell'applicazione di un fattore non indifferente. E' ragionevole sincronizzare le richieste solo se l'istanza del Singleton è nulla. La soluzione definitiva è dunque:

public static MyDBConnector getConnector() throws Exception {
if(instance==null)
synchronized (MyDBConnector.class) {
if(instance==null)
instance = new MyDBConnector();
}
return instance;
}

martedì 4 novembre 2008

Design Patterns

Nell'Ingegneria del Software un Design Patterns può essere definito come "una soluzione progettuale generale a un problema ricorrente". Tale soluzione è language-independent e rappresenta un modello da applicare per risolvere un problema che può presentarsi in diverse situazioni durante la progettazione e lo sviluppo del software. La GOF(Gang Of Four) fu il primo gruppo di sviluppatori a pensare di definire un insieme di formalismi che permettesse di facilitare la progettazione e sviluppo del software basata su un insieme di Design Pattern. SUN suggerisce lo studio del libro scritto dalla GOF, "Design Patterns"- Elements of Reusable Object-Oriented Software, e proprio da esso si prende spunto per descrivere i seguenti Design Pattern, con esempi concreti di utilizzo implementati in Java, il piu popolare dei linguaggi OO.

La GOF ha definito un insieme di 23 Design Pattern ben noti, suddivisi in 3 grandi categorie (in rosso quelli già trattati in questo post):

-Pattern Creazionali(Singleton, Abstract Factory, Builder, Factory, Prototype)

-Pattern Strutturali(Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy)

-Pattern Comportamentali(Chain-of-responsability, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template method, Visitor)

Tutti i post sono work-in-progress.


...POST TO BE CONTINUED....