Présentation

Un design pattern (patron de conception) est une solution de développement permettant de répondre à une problématique courante

Le design pattern Proxy permet de répondre au problème : comment adapter un traitement en fonction de l'état dans lequel se trouve mon code ?

Ce design pattern permet d'adapter les traitements effecutés en test et en production pour par exemple, ne pas joindre directement des services. C'est une solution permettant de mettre en place des objsts simulés simplement

Nous pouvons par exemple utiliser ce pattern sur la base de données pour adapter le fonctionnement en fonction de l'environnement : utiliser une base de données spécifique en production et une base de données de test dans laquelle certains comportements sont adaptés


Principe de création

Nous allons mettre en place le design patterne Proxy sur la connexion à la base de données. L'objectif est :

  1. De créer une classe ConnectionProxy
  2. Insérer une Connection dans les attributs de la classe ConnectionProxy
  3. Faire implémenter l'interface Connection à ConnectionProxy en reportant les modifications sur la Connection en paramètre

Pour l'instant, nous n'avons absolument rien changé mais il est alors possible d'adapter le comportement de la classe connection en modifiant ConnectionProxy. Voir l'exemple ci-dessous


Exemple

Nous allons ici créer un programme qui permet de modifier le comportement des connexions aux bases de données pour prendre en compte le type jsonb non connu de la base h2

Le contexte : nous avons un service qui manipule et créé une table contenant un type de données Jsonb. Il n'est a priori pas possible de tester ce code avec une base h2

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class CreationJsonb {
	public int trucAvecJsonb(Connection cnx) throws SQLException {
		try (Statement stmt = cnx.createStatement()) {
			int res = 0;
			stmt.executeUpdate("CREATE TABLE test(id serial, liste jsonb)");
			stmt.executeUpdate("INSERT INTO test(liste) VALUES ('{\"val1\": 1}'), ('{\"val1\": 2}')");
			try (ResultSet rs = stmt.executeQuery("SELECT * FROM test")) {
				while (rs.next()) {
					res++;
					System.out.println(rs.getInt("id") + ":" + rs.getString("liste"));
				}
			}
			return res;
		}
	}
}

Nous aurions envie de réaliser tout de même un test sur cette classe. Pour celà, nous allons utiliser un proxy de connection de la manière suivante

public class ConnectionProxy implements Connection {
	private Connection connection;
	private boolean test;

	public ConnectionProxy(Connection connection) {
		super();
		this.connection = connection;
	}

	public boolean isTest() {
		return test;
	}

	public void setTest(boolean test) {
		this.test = test;
	}

	@Override
	public Statement createStatement() throws SQLException {
		return new StatementProxy(connection.createStatement(), test);
	}

	@Override
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		return connection.prepareStatement(sql);
	}
	
	// Autres méthodes de Connection
}

public class StatementProxy implements Statement {
	private Statement stmt;
	private boolean test;

	public StatementProxy(Statement stmt, boolean test) {
		this.stmt = stmt;
		this.test = test;
	}

	@Override
	public int executeUpdate(String sql) throws SQLException {
		if (test) {
			return stmt.executeUpdate(sql.replace("jsonb", "text"));
		}
		else {
			return stmt.executeUpdate(sql);
		}
	}
	
	// Autres méthodes de statement
}

Nous n'avons plus qu'à réaliser le test en indiquant la la connection que nous sommes dans l'environnement de test et de manière transparente, le type de données sera adapté

public class CreationJsonbTest {
	@Test
	public void testerService() {
		JdbcDataSource dataSource = new JdbcDataSource();
		dataSource.setURL("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
		dataSource.setUser("sa");
		dataSource.setPassword("");
		try (ConnectionProxy cnx = new ConnectionProxy(dataSource.getConnection())) {
			cnx.setTest(true);
			int nb = new CreationJsonb().trucAvecJsonb(cnx);
			Assertions.assertEquals(2, nb);
		}
		catch (Exception e) {
			e.printStackTrace();
			Assertions.fail();
		}
	}
}