IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Design Patterns Discussion :

Design Pattern pour gérer des connexions aux BD


Sujet :

Design Patterns

  1. #1
    Futur Membre du Club
    Inscrit en
    Janvier 2010
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 10
    Points : 5
    Points
    5
    Par défaut Design Pattern pour gérer des connexions aux BD
    Bonjour,

    Je gère une application qui va faire des traitements sur plusieurs bases de données.

    J'ai une classe DbConnection qui gére la connexion aux bases de données. Je voudrais que cette classe ne m'instancie qu'une connexion pour chaque base.

    Je sens que je suis proche du Design Pattern Singleton mais je ne sais pas s'il est vraiment approprié ou si je l'ai mal mis en œuvre. En tous cas la classe que j'ai écrite veut s'occuper à la fois la gestion de l'instanciation unique de chaque connexion et la gestion de la connexion elle-même et je sens bien que ce n'est pas ce qu'il faut faire.

    Voici la classe que j'ai écrite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    package com.bdd.util;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.sql.DataSource;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class DbConnection {
    
    	private static Log log = LogFactory.getLog(DbConnection.class);
    
    	private String jndiName;
    	private DataSource source;
    	private Connection connexion;
    	private DbConnection[] instances;
    
    	/**
    	 * Constructeur
    	 */
    	 private DbConnection(String jndiName) {
    		this.jndiName = jndiName;
    
    		try {
    			 InitialContext ic = new InitialContext();
    			 this.source = (DataSource) ic.lookup(jndiName);
    			 this.connexion = source.getConnection();
    			 this.connexion.setAutoCommit(false);
    			 log.info(">>> DbConnection :: Connexion à la DataSource '"
    			 + this.jndiName + "' <<<");
    
    		}
    		 catch (NamingException e) {
    		 e.printStackTrace();
    		 log.error(">>> DbConnection :: Echec de la connexion à la DataSource '" + this.jndiName + "' <<<" + e.getMessage());
    		 }
    		catch (SQLException e) {
    			e.printStackTrace();
    			 log.error(">>> DbConnection :: Echec de la connexion à la DataSource '" + this.jndiName + "' <<<" + e.getMessage());
    		}
    	}
    
    	/**
    	 * @param jndiName
    	 *            Nom JNDI de la DataSource
    	 */
    	public DbConnection getInstance(String jndiName) {
    		if (this.instances[jndiName] == null) {
    			this.instances[jndiName] = new DbConnection(jndiName);
    		}
    		return this.instances[jndiName];
    	}
    
    	public ResultSet executeQuery(String query) {
    		log.info(">>> DbConnection.executeQuery :: Execution de la requete : '"
    				+ query);
    		ResultSet rs = null;
    		try {
    			Statement st = this.connexion.createStatement();
    			rs = st.executeQuery(query);
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return rs;
    	}
    
    	public int executeUpdate(String query) {
    		log.info(">>> DbConnection.executeUpdate :: Execution de la requete : '"
    						+ query);
    		int updatedRows = 0;
    		try {
    			Statement st = this.connexion.createStatement();
    			updatedRows = st.executeUpdate(query);
    			log.info(">>> DbConnection.executeUpdate :: Nombre de lignes modifiées : '"
    							+ updatedRows);
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return updatedRows;
    	}
    
    	public void closeResultSet(ResultSet rs) {
    		try {
    			rs.close();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    	public void closeConnection() {
    		try {
    			this.connexion.close();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    	public void commit() {
    		try {
    			this.connexion.commit();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    	public void rollback() {
    		try {
    			this.connexion.rollback();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    }
    Je serais tentée de la scinder en deux :
    - Une classe DbConnection pour gérer ma connexion proprement dite,
    - Une classe DbConnectionManager qui contiendrait mon tableau de DbConnection et gèrerait l'instanciation unique de chaque connexion.
    Le problème est qu'alors je perdrais l'intérêt du fait que le singleton ne peut pas être instancié directement, car je serais alors obligée de rendre mon constructeur public.

    Voila où j'en suis de ma réflexion. J'espère avoir été suffisamment claire.

    Pouvez-vous me donner votre avis sur la manière de gérer ces connexions ?

  2. #2
    Membre expérimenté

    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    1 377
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Octobre 2005
    Messages : 1 377
    Points : 1 628
    Points
    1 628
    Par défaut
    Le singleton est un design pattern à forte connotation antipaternique (un terme inventé pour la cause ), il faut l'éviter absolument ...

    Dans ton cas, pourquoi ? Parce que tu as besoin de plus d'une instance de ton objet, tu as besoin au moins d'une instance de DBConnection par base différente ...
    Une autre raison pourrait être la grande difficulté de testé du code utilisant des singletons ...

    Et pour finir pleins d'autres raisons que tu pourras lire ici :

    http://blog.emmanueldeloget.com/inde...e-singletonite

    Ou un peu partout dans le Web

    ++

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    198
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 198
    Points : 106
    Points
    106
    Par défaut
    http://johannblais.developpez.com/tu...acces-donnees/

    Le point III.

    C'est en C# mais l'idée est tout de même exploitable.

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 442
    Points : 37 034
    Points
    37 034
    Par défaut
    Dans cette histoire vous avez bien senti qu'il faut une factory (le connexion manager) qui pour vous retourner une ou la connexion déjà ouverte à une base de donnée devra savoir quelles sont ouvertes.

    La connexion est juste un Wrapper autour de JNDI... Et vous devriez en avoir autant d'instances que souhaitable.

    La factory, ou plutôt, la structure qui va vous permettre de récupérer les connexion déjà ouvertes, ne peut être qu'unique. D'où Singleton ou pas?!?

    Mais ce qui doit être unique, ce sont les données accédées par l'instance (unique ou pas) de la factory.
    Vous pouvez le faire en déclarant une structure 'static', globale à toutes les instances de la classe 'factory'.

    Fonctionnellement, c'est pareil qu'un Singleton.
    Mais il vous permet en plus de regrouper factory et connexion dans une même classe. Alors que dans le cas Singleton classique, il faut créer deux classes, la factory étant Singleton.

    -W
    PS: Le problème du Singleton est que... données partagée = problème dans la synchronisation d'accès à celles ci. Mais bon, si vous devez "partager", vous n'avez pas d'autre choix que de faire la police après...

  5. #5
    Futur Membre du Club
    Inscrit en
    Janvier 2010
    Messages
    10
    Détails du profil
    Informations forums :
    Inscription : Janvier 2010
    Messages : 10
    Points : 5
    Points
    5
    Par défaut
    Bonjour,

    Merci pour toutes vos réponses qui m'ont été précieuses pour valider ce que j'avais fait et me faire avancer dans ma réflexion.

    Voici les classes que j'ai créées :
    - Ma Factory :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.bdd.util;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class ConnexionDbFactory {
    
    	private static Log log = LogFactory.getLog(ConnexionDbFactory.class);
    
    	private static Map<String, ConnexionDb> connexions = new HashMap<String, ConnexionDb>();
    
    	/**
    	 * Constructeur
    	 */
    	private ConnexionDbFactory() {
    	}
    
    	/**
    	 * @param jndiName
    	 *            Nom JNDI de la DataSource
    	 */
    	public static ConnexionDb getConnexion(String jndiName) {
    		if (connexions.get(jndiName) == null) {
    			connexions.put(jndiName, new ConnexionDb(jndiName));
    		}
    		return connexions.get(jndiName);
    	}
    }
    - Ma connexion :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    package com.bdd.util;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.sql.DataSource;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class ConnexionDb {
    
    	private static Log log = LogFactory.getLog(ConnexionDb.class);
    
    	private String jndiName;
    	private DataSource source;
    	private Connection connexion;
    
    	/**
    	 * Constructeur
    	 * 
    	 * @param jndiName
    	 *            Nom JNDI de la DataSource
    	 */
    	public ConnexionDb(String jndiName) {
    		super();
    		this.jndiName = jndiName;
    
    		try {
    			InitialContext ic = new InitialContext();
    			this.source = (DataSource) ic.lookup(jndiName);
    			this.connexion = source.getConnection();
    			this.connexion.setAutoCommit(false);
    			log.info(">>> ConnexionDb :: Connexion à la DataSource '"
    					+ this.jndiName + "' <<<");
    
    		} catch (NamingException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			log
    					.error(">>> ConnexionDb :: Echec de la connexion à la DataSource '"
    							+ this.jndiName + "' <<<" + e.getMessage());
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			log
    					.error(">>> ConnexionDb :: Echec de la connexion à la DataSource '"
    							+ this.jndiName + "' <<<" + e.getMessage());
    		}
    	}
    
    	public ResultSet executeQuery(String query) {
    		log.info(">>> ConnexionDb.executeQuery :: Execution de la requete : '"
    				+ query);
    		ResultSet rs = null;
    		try {
    			Statement st = this.connexion.createStatement();
    			rs = st.executeQuery(query);
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return rs;
    	}
    
    	public int executeUpdate(String query) {
    		log
    				.info(">>> ConnexionDb.executeUpdate :: Execution de la requete : '"
    						+ query);
    		int updatedRows = 0;
    		try {
    			Statement st = this.connexion.createStatement();
    			updatedRows = st.executeUpdate(query);
    			log
    					.info(">>> ConnexionDb.executeUpdate :: Nombre de lignes modifiées : '"
    							+ updatedRows);
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return updatedRows;
    	}
    
    	public void closeResultSet(ResultSet rs) {
    		try {
    			rs.close();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    	public void closeConnection() {
    		try {
    			this.connexion.close();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    	public void commit() {
    		try {
    			this.connexion.commit();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    	public void rollback() {
    		try {
    			this.connexion.rollback();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    Pour appeler ma connexion de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ConnexionDb cnx = ConnexionDbFactory.getConnexion("java:maDatasource");
    ResultSet rs = cnx.executeQuery("SELECT * FROM ma_table");
    Je n'ai pas bien compris comment manipuler ma connexion si je la regroupais dans la même classe que ma Factory. Si je comprends bien, ConnexionDb deviendrait une classe interne static de ma Factory et mon code appelant n'instancierait donc pas la classe ConnexionDb mais la manipulerait à travers la Factory. L'intérêt serait alors de ne plus exposer le constructeur de ma classe ConnexionDb. Ma classe deviendrait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    package com.bdd.util;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.sql.DataSource;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    public class ConnexionDbFactory {
    
    	private static Log log = LogFactory.getLog(ConnexionDbFactory.class);
    
    	private static Map<String, ConnexionDb> connexions = new HashMap<String, ConnexionDb>();
    
    	/**
    	 * Constructeur
    	 */
    	private ConnexionDbFactory() {
    	}
    
    	/**
    	 * @param jndiName
    	 *            Nom JNDI de la DataSource
    	 */
    	public static ConnexionDb getConnexion(String jndiName) {
    		if (connexions.get(jndiName) == null) {
    			connexions.put(jndiName, new ConnexionDb(jndiName));
    		}
    		return connexions.get(jndiName);
    	}
    
    	public static class ConnexionDb {
    
    		private String jndiName;
    		private DataSource source;
    		private Connection connexion;
    
    		/**
    		 * Constructeur
    		 * 
    		 * @param jndiName
    		 *            Nom JNDI de la DataSource
    		 */
    		private ConnexionDb(String jndiName) {
    			super();
    			this.jndiName = jndiName;
    
    			try {
    				InitialContext ic = new InitialContext();
    				this.source = (DataSource) ic.lookup(jndiName);
    				this.connexion = source.getConnection();
    				this.connexion.setAutoCommit(false);
    				log.info(">>> ConnexionDb :: Connexion à la DataSource '"
    						+ this.jndiName + "' <<<");
    
    			} catch (NamingException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    				log
    						.error(">>> ConnexionDb :: Echec de la connexion à la DataSource '"
    								+ this.jndiName + "' <<<" + e.getMessage());
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    				log
    						.error(">>> ConnexionDb :: Echec de la connexion à la DataSource '"
    								+ this.jndiName + "' <<<" + e.getMessage());
    			}
    		}
    
    		public ResultSet executeQuery(String query) {
    			log
    					.info(">>> ConnexionDb.executeQuery :: Execution de la requete : '"
    							+ query);
    			ResultSet rs = null;
    			try {
    				Statement st = this.connexion.createStatement();
    				rs = st.executeQuery(query);
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			return rs;
    		}
    
    		public int executeUpdate(String query) {
    			log
    					.info(">>> ConnexionDb.executeUpdate :: Execution de la requete : '"
    							+ query);
    			int updatedRows = 0;
    			try {
    				Statement st = this.connexion.createStatement();
    				updatedRows = st.executeUpdate(query);
    				log
    						.info(">>> ConnexionDb.executeUpdate :: Nombre de lignes modifiées : '"
    								+ updatedRows);
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			return updatedRows;
    		}
    
    		public void closeResultSet(ResultSet rs) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    
    		public void closeConnection() {
    			try {
    				this.connexion.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    
    		public void commit() {
    			try {
    				this.connexion.commit();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    
    		public void rollback() {
    			try {
    				this.connexion.rollback();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    
    	}
    }
    Et l'appel serait alors :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ConnexionDbFactory.ConnexionDb cnx = ConnexionDbFactory.getConnexion("java:maDatasource");
    ResultSet rs = cnx.executeQuery("SELECT * FROM ma_table");
    Est-ce bien ce que vous avez voulu dire ?

    Il me reste maintenant à gérer l'unicité de mon closeConnection() : il ne faut pas que je ferme ma connexion si elle peut encore être utilisée par le code appelant (dans le cas de traitements imbriqués). Je pensais donc gérer dans ma Factory un tableau de booléens remplis lors de l'ouverture de connexion et vidé lors de la fermeture de connexion. Je ne fermerais réellement alors ma connexion que si le tableau est vide. Cela donnerait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    public class ConnexionDbFactory {
    [...]
    	private static Map<String, ConnexionDb> connexions = new HashMap<String, ConnexionDb>();
    	private static Map<String, List<Boolean>> connecte = new HashMap<String, List<Boolean>>();
    
    [...]
    	/**
    	 * @param jndiName
    	 *            Nom JNDI de la DataSource
    	 */
    	public static ConnexionDb getConnexion(String jndiName) {
    		if (connexions.get(jndiName) == null) {
    			connexions.put(jndiName, new ConnexionDb(jndiName));
    		}
    		if(connecte.get(jndiName) == null) {
    			connecte.put(jndiName, new ArrayList<Boolean>());
    		}
    		connecte.get(jndiName).add(Boolean.TRUE);
    		return connexions.get(jndiName);
    	}
    
    	public void closeConnection(String jndiName) {
    		if (connexions.get(jndiName) != null) {
    			if (connecte.get(jndiName) == null
    					|| connecte.get(jndiName).isEmpty()) {
    				connexions.get(jndiName).closeConnection();
    			} else {
    				connecte.get(jndiName)
    						.remove(connecte.get(jndiName).size() - 1);
    
    			}
    		}
    	}
    
    	public static class ConnexionDb {
    
    [...]
    		private void closeConnection() {
    			try {
    				this.connexion.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    [...]
    	}
    }
    Cette méthode est-elle une bonne pratique ?

    Cordialement

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 442
    Points : 37 034
    Points
    37 034
    Par défaut
    Je n'ai pas bien compris comment manipuler ma connexion si je la regroupais dans la même classe que ma Factory. Si je comprends bien, ConnexionDb deviendrait une classe interne static de ma Factory et mon code appelant n'instancierait donc pas la classe ConnexionDb mais la manipulerait à travers la Factory. L'intérêt serait alors de ne plus exposer le constructeur de ma classe ConnexionDb.
    Vous avez une classe ConnexionDb et une méthode static ConnexionDb.getInstance qui en fonction de l'état de la Map crée une connexion ou en retourne une déjà existante.
    Le constructeur de ConnexionDb n'est exposé qu'a ConnexionDb.getInstance.

    Il me reste maintenant à gérer l'unicité de mon closeConnection() : il ne faut pas que je ferme ma connexion si elle peut encore être utilisée par le code appelant (dans le cas de traitements imbriqués). Je pensais donc gérer dans ma Factory un tableau de booléens remplis lors de l'ouverture de connexion et vidé lors de la fermeture de connexion. Je ne fermerais réellement alors ma connexion que si le tableau est vide
    Associez à chaque connexion ouverte un compteur de référence ref_counter.
    Chaque fois que vous retourner une connexion existante vous augmentez le ref_counter et chaque fois qu'on fait close vous le décrémentez.
    Lorsque ref_counter passe à zéro, vous fermez la connexion ou vous démarrez un timer qui la fermera un peu plus tard si ref_counter est encore 0.

    Cette méthode est-elle une bonne pratique ?
    Si la question porte sur le ref_counter, oui modulo que vous avez encore une variable partagée à protéger si vous voulez utiliser des threads.

    Pour ce qui est de votre question initiale et de la réponse que je vous ai donnée... Le plus simple est de séparer factory et connexion en faisant de factory un Singleton.
    Vous n'utilisez que des instances de...

    La factorisation de la factory dans la connexion est quelque chose qui demande une certaine maturité?
    On fusionne deux choses "logiquement" séparées en utilisant des propriétés naturelles d'instance et de classe.

    Rien de très compliqué en soi, mais vous vous rendez compte en faisant vous même l'exercice que ce n'est pas si "naturel" et peut être pas si "simple".

    Vous avez vu aussi que cela s'appuie sur une sorte de Singleton d'une autre nature que le Singleton qu'il y a dans les bouquins: il autorise plein d'instances différentes mais réalise la propriété "Singleton" via un état global partagé (stocké dans les variables de "classes").
    Note: Le Singleton des bouquins utilise une variable de "classe" pour y stocker l'unique instance du Singleton.
    -W
    PS: Ce type de Singleton a pour nom "Borg design pattern" - Borg étant celui de son inventeur. Mais depuis le monde Java, c'est probablement la Zone 51 - vous savez la ou les USA cachent les technos aliens -.

  7. #7
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Vous avez vu aussi que cela s'appuie sur une sorte de Singleton d'une autre nature que le Singleton qu'il y a dans les bouquins: il autorise plein d'instances différentes mais réalise la propriété "Singleton" via un état global partagé (stocké dans les variables de "classes").
    Note: Le Singleton des bouquins utilise une variable de "classe" pour y stocker l'unique instance du Singleton.
    -W
    PS: Ce type de Singleton a pour nom "Borg design pattern" - Borg étant celui de son inventeur. Mais depuis le monde Java, c'est probablement la Zone 51 - vous savez la ou les USA cachent les technos aliens -.
    Ce patron "Borg Singleton" ressemble à s'y méprendre à un patron de conception appelée "monostate", l'idée de base étant de partager un état à travers touts les instances de la classe. Ca ne résout pas les différents problèmes liés au singleton puisque c'est strictement équivalent à encapsuler les accès au singleton dans une classe, mais ce pattern gagne d'un court cheveux dès lors qu'il faut effectuer certain refactorings.

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 442
    Points : 37 034
    Points
    37 034
    Par défaut
    Bonjour,

    Citation Envoyé par Emmanuel Deloget Voir le message
    Ce patron "Borg Singleton" ressemble à s'y méprendre à un patron de conception appelée "monostate", l'idée de base étant de partager un état à travers touts les instances de la classe.
    Merci pour l'information, et après avoir trouvé à quoi ressemble ce patron sur Google... c'est effectivement la même chose.
    Mais son nom est discutable car "shared state" me semble correspondre plus à ce qu'il est effectivement que "mono state".

    Ca ne résout pas les différents problèmes liés au singleton puisque c'est strictement équivalent à encapsuler les accès au singleton dans une classe
    Ce sont des sujets liés à la nature d'un singleton quel qu'en soit la forme: derrière se cache une entité "unique". Et les questions relative au partage par les différents utilisateurs de l'état correspondant surtout dans le cas où les accès peuvent être asynchrones ou non atomiques.

    mais ce pattern gagne d'un court cheveux dès lors qu'il faut effectuer certain refactorings.
    Alors là je veux bien que vous éclairiez ma lanterne car je n'arrive pas à percevoir cet avantage.

    - W
    PS: je vous ferais grâce (pour l'instant) d'une 3ème forme de Singleton car je viens de m'apercevoir qu'il touche à des fondamentaux que je suis pour l'instant incapable de raconter

  9. #9
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Alors là je veux bien que vous éclairiez ma lanterne car je n'arrive pas à percevoir cet avantage.
    Puisqu'il encapsule l'accès au singleton, il permet de dissocier le singleton du code client. Si plus tard on trouve une autre manière de récupérer les informations associées, le changement de code est plus local - donc plus aisé.

    Le singleton devient un détail d'implémentation encapsulé dans une abstraction de plus haut niveau - ce qui n'est plus le cas si on utilise directement le singleton. C'est une mise en pratique minimaliste du principe d'inversion de dépendance.

  10. #10
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 442
    Points : 37 034
    Points
    37 034
    Par défaut
    Bonjour

    Merci pour votre éclairage.

    Citation Envoyé par Emmanuel Deloget Voir le message
    Puisqu'il encapsule l'accès au singleton, il permet de dissocier le singleton du code client. Si plus tard on trouve une autre manière de récupérer les informations associées, le changement de code est plus local - donc plus aisé.
    Ah je vois. Nous avons un problème de vocabulaire.
    Pour moi le singleton est la chose qui encapsule les accès aux données partagées. Elles sont dissociées du code client de la même façon dans les deux cas.

    Le singleton devient un détail d'implémentation encapsulé dans une abstraction de plus haut niveau - ce qui n'est plus le cas si on utilise directement le singleton. C'est une mise en pratique minimaliste du principe d'inversion de dépendance.
    J'ai mis une URL vers ce que j'ai trouvé de pas trop compliqué expliquant DIP - si vous trouvez mieux n'hésitez pas.

    Je n'ai pas encore trituré suffisamment en détails les fondamentaux dont je parlais ce matin pour raconter de façon intelligible ce qui me tracasse dans cette histoire.
    Donc vous avez certainement raison

    -W

  11. #11
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    255
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 255
    Points : 99
    Points
    99
    Par défaut
    bonjour,
    ce sujet est intéressant car il est assez typique de la problématique conceptuelle vécue par les personnes qui rentrent dans le monde de la POO.
    Moi même je suis souvent tente de passez par le Singleton, ce qui est un mauvais réflexe et une preuve de la faiblesse de la conception du logiciel.

    J'ai lu quelque part une règle très bonne sur comment bien concevoir : c'est de programmmer le plus possible des interfaces. L'idée est de mettre des interfaces partout où on a une ouverture de la classe sur l'extérieur. De cette façon on assure la capacité de remplacer une implémentation par une autre.

    Sur le problème spécifique du Singleton, il y a plusieurs question a se poser sur pourquoi on y pense :
    1) pour limiter le nombre de paramètres des méthodes qui accèdent a la classe (la globalite aiguë, se soigne avec un constructeur prenant l'objet en paramètre, ou a la limite une méthode setMachin(Machinable p_machin), une variable d'instance et un check dans les méthodes qui y accèdent
    2) pour limiter le nombre d'instances : pourquoi limiter les instances ? si c'est vraiment nécessaire, on peut résoudre ça en positionnant un flag static sur la classe qu'on positionnera lors de la création d'une première instance et qui sera vérifiée lors des instanciations suivantes (attention section critique en multithreading)
    3) pour un faire "manager" : une instance fait aussi bien, et si on veut y accéder "partout" on retombe sur la globalite aiguë...

    A part ça je ne vois pas d'autre cause a cette maladie de POO

Discussions similaires

  1. Les design pattern pour créer des jeux
    Par alex6891 dans le forum Design Patterns
    Réponses: 4
    Dernier message: 26/11/2018, 20h59
  2. Réponses: 1
    Dernier message: 20/06/2014, 01h48
  3. Design pattern pour la synchronisation des données
    Par cedrix57 dans le forum Design Patterns
    Réponses: 2
    Dernier message: 23/11/2012, 13h31
  4. Design pattern pour la synchronisation des données
    Par cedrix57 dans le forum Modélisation
    Réponses: 0
    Dernier message: 04/04/2012, 12h45
  5. Un design pattern pour des appels à un web service
    Par kekranx dans le forum Design Patterns
    Réponses: 0
    Dernier message: 07/07/2008, 10h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo