venerdì 28 novembre 2008

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;
}

Nessun commento: