<?xml version="1.0" encoding="ISO-8859-1"?>

<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<title>Forum du club des développeurs et IT Pro - Blogs - PhilippeGibault</title>
		<link>https://www.developpez.net/forums/blogs/280180-philippegibault/</link>
		<description>Developpez.com, le Club des Développeurs et IT Pro</description>
		<language>fr</language>
		<lastBuildDate>Sun, 26 Apr 2026 21:06:38 GMT</lastBuildDate>
		<generator>vBulletin</generator>
		<ttl>15</ttl>
		<image>
			<url>https://forum.developpez.be/images/misc/rss.jpg</url>
			<title>Forum du club des développeurs et IT Pro - Blogs - PhilippeGibault</title>
			<link>https://www.developpez.net/forums/blogs/280180-philippegibault/</link>
		</image>
		<item>
			<title>Parlons un peu des DAO</title>
			<link>https://www.developpez.net/forums/blogs/280180-philippegibault/b10370/parlons-dao/</link>
			<pubDate>Fri, 12 Aug 2022 11:27:46 GMT</pubDate>
			<description>*Introduction* 
Dernièrement,...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore"><b>Introduction</b><br />
Dernièrement, je travaillais sur un projet qui avait une dette technique importante. Le deuxième projet le plus endetté techniquement sur lequel je suis intervenu. <br />
Ce projet a d'ailleurs eu (littéralement) ma peau.<br />
J'avais des usines à gaz, des librairies (JSF, Hibernate) mal compris et pas utilisées de manière pertinente, SOLID pas utilisé, de l'objet divin, des rôles mal définit, de la coulé de lave...<br />
<br />
Mais un point particulier m'a interpelé: Les gens qui avaient fait le projet ne savait pas ce qu'était une DAO (Pour Data Acces Object ou en français objet d'accès aux données <a href="https://fr.wikipedia.org/wiki/Objet_d%27acc%C3%A8s_aux_donn%C3%A9es" target="_blank">https://fr.wikipedia.org/wiki/Objet_...x_donn%C3%A9es</a> ).<br />
En particulier, il y avait confusion entre la DAO et le template method de la DAO. La différence est fine, pourtant, elle est importante.<br />
<br />
De fait, un controleur pouvait avoir accès au SQL ou JPQL (en résumé grossier, le SQL de Hibernate JPA) ce qui pour moi est juste une hérésie. Le contrôleur n'a pas à savoir la BDD qui est derrière.<br />
<br />
A la décharge, j'ai fais un tour d'Internet sur le sujet et j'ai vu peu de lien qui expliquait vraiment ce qu'est une DAO.<br />
<br />
De plus, j'ai souvent vu la confusion entre la DAO et le Template Method DAO. De fait, je me dis que ça serait bien d'apporter ma pierre à l'édifice, et de faire un topo de la question.<br />
<br />
Ici, on va donc parler DAO. Mais d'abord, on va faire un rappel de certains principes de programmations, d'UML, de BDD. Ensuite, on implémentera (pour mon projet) des DAO, et on fera aussi au passage, des tests unitaires.<br />
<br />
<b>1) SOLID</b><br />
Premier principe à garder en mémoire, c'est SOLID ( <a href="https://fr.wikipedia.org/wiki/SOLID_(informatique)" target="_blank">https://fr.wikipedia.org/wiki/SOLID_(informatique)</a> ).<br />
<br />
Le S (pour Single responsibility principle soit Principe de responsabilité unique) dit qu'une classe, méthode ou fonction ne doit avoir qu'une seule et unique responsabilité.<br />
<br />
Le O (pour Open/closed principle ou Principe ouvert/fermé) dit que mon objet doit être fermé à la modification directe mais ouvert à l'extension.<br />
<br />
Le L (pour Liskov substitution principle ou principe de substitution de Liskov <a href="https://fr.wikipedia.org/wiki/Principe_de_substitution_de_Liskov" target="_blank">https://fr.wikipedia.org/wiki/Princi...tion_de_Liskov</a> ) est plus théorique, mais tout aussi important.<br />
Une instance de type T peut être remplacée par une instance de type G, G étant un sous type de T, sans modifier la cohérence du programme.<br />
<br />
Le I (pour Interface segregation principle ou Principe de ségrégation des interfaces)  dit qu'il faut préférer plusieurs interfaces spécifiques pour chaque client plutôt qu'une seule interface générale.<br />
<br />
Enfin, le D (Dependency inversion principle ou Principe d'inversion de dépendance) dit qu'il faut dépendre des abstraction, pas des implémentations. <br />
Ce dernier principe est à la base des moteurs d'injections de dépendances comme Spring ou les EJB.<br />
<br />
<b>2) Masquage de l'information</b><br />
<a href="https://fr.wikipedia.org/wiki/Masquage_de_l%27information" target="_blank">https://fr.wikipedia.org/wiki/Masqua...%27information</a><br />
Ce principe explique qu'il faut cacher les détails d'implémentation afin d'offrir à un utilisateur une interface simple à comprendre et à utiliser.<br />
<br />
Il va avec le D de SOLID, vu que l'on va injecter des interfaces et non pas des classes ou des classe abstraites.<br />
Il va avec le L, car l'implémentation est un sous type de l'interface.<br />
<br />
C'est pour ça qu'il faut définir comme type une List (Interface Java) et non une ArrayList (Une des implémentation).<br />
<br />
Ce principe est à mettre en parallèle avec le modèle OSI ( <a href="https://fr.wikipedia.org/wiki/Mod%C3%A8le_OSI" target="_blank">https://fr.wikipedia.org/wiki/Mod%C3%A8le_OSI</a> ) dans les réseaux informatiques.<br />
On offre sur la couche au dessus une interface, soit des méthodes. La couche au dessus ne connait pas le fonctionnement de la couche en dessous, et donc son fonctionnement. <br />
<br />
<b>3) Notion d'interface</b><br />
L'interface est d'abord une notion d'UML (<a href="https://laurent-audibert.developpez.com/Cours-UML/?page=diagramme-classes#L3-2-4" target="_blank">https://laurent-audibert.developpez....classes#L3-2-4</a>). C'est les services extérieurs offert à un utilisateur. <br />
Notez qu'en Java, c'est aussi un type. <br />
Mais en Java, c'est la même chose qu'en UML. Une interface, c'est un service que peut utiliser un utlisateur extérieur.<br />
<br />
De plus, implémenter une interface, c'est répondre à un contrat. C'est avoir des méthodes implémentés comme définies par le contrat.<br />
Il va de soit qu'à cause du D de SOLID, et du point 2, un programme ne doit voir que une interface, pas son implémentation.<br />
<br />
<b>4)Rappel 1 de la BDD: CRUD</b><br />
Dans une base de données (sous entendu ici relationnelle, mais ça peut-être No-SQL ou XML), on fait toujours la même chose.<br />
On fait du CRUD ( <a href="https://fr.wikipedia.org/wiki/CRUD" target="_blank">https://fr.wikipedia.org/wiki/CRUD</a> ) .<br />
<br />
On crée en BDD (C pour create soit créer).<br />
On lit en base de donnée (R pour read soit lire).<br />
On met à jour en BDD (U pour update soit mettre à jour).<br />
On efface en BDD (D pour delete soit effacer).<br />
<br />
Le CRUD sera vital pour définir une DAO.<br />
<br />
<b>5)Rappel 2 pour une BDD: ACID (BDD relationnelles seulemnt)</b><br />
Une BDD relationnelle a un principe immuable: elle est toujours cohérente.<br />
C'est tout ou rien!<br />
<br />
Effectivement, imaginez que vous faites une opération bancaire. Vous déboursé 100€. Mais là, il survient une panne. Ce qui fait que la personne à qui vous payez 100€ ne reçoit pas les 100€.<br />
Mais vous, vous les avez débitez. Je pense que vous ne seriez pas content.<br />
 <br />
C'est pour ça qu'en BDD, c'est tout ou rien. Si il y a une panne, on revient à l'état initial, l'état stable. On dit que l'on &quot;Rollback&quot;.<br />
<br />
Les opérations, en BDD, se font dans ce que l'on appelle une transaction. Si tout est validé, on valide le nouvel état stable. On dit que l'on &quot;Commit&quot;.<br />
<br />
La transaction applique le principe ACID:<br />
A pour atomicité. C'est tout ou rien.<br />
<br />
C pour cohérence. Les états visibles en BDD sont toujours cohérents.<br />
<br />
I pour isolation. Si on a deux transactions, alors elles sont indépendantes.<br />
<br />
D pour durabilité. Une fois que la transaction est validée, c'est pour toujours.<br />
<br />
ACID n'est pas nécessaire pour définir une DAO, mais va devenir primordiale lorsque l'on passera à l'implémentation.<br />
<br />
<b>6) Définition d'une DAO</b><br />
On peut enfin définir une DAO.<br />
<div class="bbcode_container">
	<div class="bbcode_quote">
		<div class="quote_container">
			<div class="bbcode_quote_container"></div>
			
				Une DAO est une interface qui pour un objet persistant, permet de faire du CRUD et d'autres opérations (recherche par nom...)
			
		</div>
	</div>
</div>Par exemple, ceci est une DAO:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:192px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td valign="top"><pre style="margin: 0">
public interface Dao&lt;T&gt; {
    
    Optional&lt;T&gt; get(long id);
    
    List&lt;T&gt; getAll();
    
    void save(T t);
    
    void update(T t, String[] params);
    
    void delete(T t);
}</pre></td></tr></table></pre>
</div>Par contre, ceci n'est pas, pour moi, une bonne DAO. C'est une classe abstraite et pire, la connexion en BDD est publique, donc visible de l'extérieure. Comme par un contrôleur par exemple...<br />
Je rappelle aussi que selon Joshua Bloch ( <a href="https://www.amazon.fr/Effective-Java-Joshua-Bloch/dp/0134685997" target="_blank">https://www.amazon.fr/Effective-Java.../dp/0134685997</a> ), il faut préférer les interfaces aux classes abstraites.<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br /></div></td><td valign="top"><pre style="margin: 0">
package com.developpez.dao;

import java.sql.Connection;
import com.developpez.jdbc.ConnectionPostgreSQL;

public abstract class DAO&lt;T&gt; {

    public Connection connect = ConnectionPostgreSQL.getInstance();
    
    /**
     * Permet de récupérer un objet via son ID
     * @param id
     * @return
     */
    public abstract T find(long id);
    
    /**
     * Permet de créer une entrée dans la base de données
     * par rapport à un objet
     * @param obj
     */
    public abstract T create(T obj);
    
    /**
     * Permet de mettre à jour les données d'une entrée dans la base 
     * @param obj
     */
    public abstract T update(T obj);
    
    /**
     * Permet la suppression d'une entrée de la base
     * @param obj
     */
    public abstract void delete(T obj);
}

}</pre></td></tr></table></pre>
</div><b>7) Conséquences de cette définition</b><br />
C'est pour un objet persistent. C'est à dire qui peut être sauvé en BDD.<br />
On respecte le point 2. L'utilisateur ne voit que les méthodes pour persister l'objet.<br />
<br />
Je n'ai pas précisé la BDD (qui peut être MySQL, Oracle, PostgreSQL, ou une No-SQL comme MongoDB, voir des fichiers XML).<br />
Je n'ai pas précisé l'implémentation. Ça peut-être du JDBC pur et dur, ou un ORM (comme Hibernate JPA).<br />
<br />
La seule chose sure, c'est que pour un utilisateur (comprendre une classe) à qui j'injecterais la classe, il pourra faire du CRUD.<br />
Et la personne qui doit faire du CRUD n'a pas besoin de savoir comment on fait du CRUD. Il a juste besoin de pouvoir le faire.<br />
<br />
<b>8) Mon implémentation dans le projet</b><br />
Pour commencer, je ne fait pas du CRUD avec n'importe quoi. Je le fais avec des entité (un terme JPA).<br />
<br />
En BDD, on a un identifiant. Je définis donc un contrat que respectera toutes les entités:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.base.entity;

/**
 * Représente une entité en BDD
 * @author phili
 *
 */
public interface IEntity &lt;I&gt;{

	/**
	 * 
	 * @return L'identifiant en BDD
	 */
	I getId();
	
	/**
	 * Set l'id en BDD
	 * @param id L'id à setter
	 */
	void setId(I id);
}</pre></td></tr></table></pre>
</div>A partir de là, je définis la DAO:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.base.dao;

import java.util.List;

import com.calculateur.warhammer.base.entity.IEntity;
import com.calculateur.warhammer.base.exception.DAOException;
import com.calculateur.warhammer.base.exception.FunctionnalExeption;

/**
 * DAO pour une entité en BDD
 * @author phili
 *
 * @param &lt;I&gt; Type de l'id de l'entité
 * @param &lt;E&gt; L'entité
 */
public interface IDAO &lt;I,E extends IEntity&lt;I&gt;&gt;{

	/**
	 * Sauvegarde l'entité en BDD (Création)
	 * @param entity L'entité à sauvegarder
	 * @return L'entité à créer
	 * @throws DAOException Si problèeme en BDD
	 * @throws FunctionnalExeption Si problème à la vérification avant d'insérer en BDD
	 */
	E save(E entity)throws DAOException,FunctionnalExeption;
	
	/**
	 * Met à jour l'entit en BDD
	 * @param entity L'entité à modifier
	 * @return L'entité modifiée
	 * @throws DAOException Si problème en BDD
	 * @throws FunctionnalExeption Si problème avant de vérifier l'entité
	 */
	E update(E entity)throws DAOException,FunctionnalExeption;
	
	/**
	 * 
	 * @param id Id de l'entité
	 * @return L'id correspondant à l'entité (null sinon)
	 * @throws DAOException Si problème en BDD
	 */
	E getById(I id) throws DAOException;
	
	/**
	 * 
	 * @return La liste des entité présente en BDD
	 * @throws DAOException Si problème en BDD
	 */
	List&lt;E&gt; getAll() throws DAOException;
	
	/**
	 * Efface en BDD l'entité correspondant à l'identifiant
	 * @param id L'id de l'entité
	 * @throws DAOException Si problème en BDD
	 */
	void delete(I id)throws DAOException;
	
	/**
	 * 
	 * @return Le nombre d'occurence en BDD
	 * @throws DAOException
	 */
	Long count()throws DAOException;	
}</pre></td></tr></table></pre>
</div>Je ne vais pas aborder les exceptions (c'est un sujet très complexe), mais pour moi, ça peut bien se passer, mais ça peut aussi mal se passer. <br />
C'est pour ça que je définit dans l'interface les exceptions qui sont susceptibles d'être jetées.<br />
<br />
J'en distingue 2:<br />
<ul><li style="">Les<i>DAOException</i> qui sont jetées si il y a un problème avec la BDD (elle tombe en panne, mauvais SQL...)</li><li style="">Les <i>FunctionnalExeption</i> qui sont jetées si l'utilisateur, lors d'une modification ou d'une création, essaye de mettre un objet non cohérent.</li></ul><br />
<br />
<b>9) Des Tests</b><br />
Bon, il faut tester. Et comme on va à peut près tester la même chose, on va utiliser le Design Patter Template Method (qui là justifie une classe abstraite) pour faire le TU.<br />
Soit:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.base.dao;

import static org.junit.jupiter.api.Assertions.fail;

import java.util.List;
import java.util.Locale;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import com.calculateur.warhammer.base.entity.IEntity;
import com.calculateur.warhammer.base.exception.FunctionnalExeption;
import com.calculateur.warhammer.base.traduction.AbstractTraductionTest;

public abstract class AbstractDAOTest &lt;I, E extends IEntity&lt;I&gt;,DAO extends IDAO&lt;I, E&gt;&gt;{

	protected final String STRING_TEST = &quot;TEST&quot;;
	
	protected final String STRING_TEST2 = &quot;TEST2&quot;;
	
	protected final String EMPTY_STRING = &quot;&quot;;
	
	protected abstract DAO getDAO();
	
	protected abstract List&lt;E&gt; getErrorsEntities();
	
	protected abstract I getIdWithNothingInDatabase();
	
	protected abstract E getEntityForInsertion();
	
	protected abstract void compareEntities(E base,E aComparer);
	
	protected abstract void updateEntity(E toUpdate);
	
	protected abstract void assertUpdate(E anEntity);
	
	protected abstract void testSupplementaires(E entityTest);
	
	@Test
	public void testDAO() {
		DAO dao = getDAO();
		//Test insertion avec erreur
		for(E entityWithError:getErrorsEntities()) {
			try {
				dao.save(entityWithError);
			}catch(Exception e) {
				Assertions.assertThat(e).isInstanceOf(FunctionnalExeption.class);
				verifieErreurFonctionnelle((FunctionnalExeption)e);
			}
		}
		try {
			final Long initial = dao.count();
			//Sauver
			E entity = getEntityForInsertion();
			entity = dao.save(entity);
			Long courant = dao.count();
			Assertions.assertThat(courant - initial).isEqualTo(1L);
			//Get By Id
			E aEntity = dao.getById(entity.getId());
			compareEntities(entity, aEntity);
			E aEntity2 = dao.getById(getIdWithNothingInDatabase());
			Assertions.assertThat(aEntity2).isNull();
			//Liste tout
			List&lt;E&gt; entities = dao.getAll();
			Assertions.assertThat(entities).hasSize(courant.intValue());
			//Test update
			updateEntity(entity);
			entity = dao.update(entity);
			aEntity = dao.getById(entity.getId());
			assertUpdate(aEntity);
			//Test supplémentaire
			testSupplementaires(aEntity);
			//Test delete
			dao.delete(entity.getId());
			courant = dao.count();
			Assertions.assertThat(initial - courant).isEqualTo(0L);
		}catch (Exception e) {
			fail(e.getMessage());
		}
	}
	
	private void verifieErreurFonctionnelle(FunctionnalExeption e) {
		for(Locale locale :AbstractTraductionTest.LOCALES_APPLICATION) {
			e.traduireLibelles(locale);
			Assertions.assertThat(e.getErreursTraduites().isEmpty()).isFalse();
		}
	}
	
	protected String getToLongString(int size) {
		StringBuilder sb = new StringBuilder();
		while(sb.length() &lt;= size) {
			sb.append(STRING_TEST);
		}
		return sb.toString();
	}
}</pre></td></tr></table></pre>
</div>Notez qu'ici, je n'ai pas définit explicitement la DAO. Je sait juste que j'ai besoin d'une DAO pour faire le test (d'où la méthode <i>getDAO()</i>).<br />
<br />
Dans un premier temps, je vais essayé d'insérer des objets en erreur, donnés par <i>List&lt;E&gt; getErrorsEntities()</i>.<br />
<br />
Puis, je vais essayer de remonter un objet qui ne correspond à rien en BDD (identifiant donné par <i>getIdWithNothingInDatabase()</i>).<br />
<br />
Ensuite, j’insère en BDD un objet (donné par <i>E getEntityForInsertion()</i>).<br />
<br />
J'essaye de l'avoir par la méthode <i>E getById(I id)</i> de la DAO et je vérifie que j'ai bien le bon objet avec le bon identifiant (méthode <i>compareEntities(E base,E aComparer)</i>).<br />
<br />
Je mets à jour l'objet (méthode <i>updateEntity(E toUpdate)</i>), puis je le récupère par identifiant, vérifiant la mise à jour (<i>assertUpdate(E anEntity)</i>).<br />
<br />
Enfin, une DAO permettant de faire d'autre truc, j'ai aussi une méthode d test pour ça (<i>testSupplementaires(E entityTest)</i>).<br />
<br />
<b>10) Le Template Method DAO</b><br />
Évidement, les méthodes de base de la DAO vont toujours faire la même chose. <br />
Comme j'ai horreur de la duplication de code, on va donner une implémentation de base pour la DAO.<br />
Pour cela, on va utiliser le Design Pattern Template Method.<br />
<br />
Soit:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br />143<br />144<br />145<br />146<br />147<br />148<br />149<br />150<br />151<br />152<br />153<br />154<br />155<br />156<br />157<br />158<br />159<br />160<br />161<br />162<br />163<br />164<br />165<br />166<br />167<br />168<br />169<br />170<br />171<br />172<br />173<br />174<br />175<br />176<br />177<br />178<br />179<br />180<br />181<br />182<br />183<br />184<br />185<br />186<br />187<br />188<br />189<br />190<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.dao.dao;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;


import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.calculateur.warhammer.base.dao.IDAO;
import com.calculateur.warhammer.base.entity.IEntity;
import com.calculateur.warhammer.base.exception.DAOException;
import com.calculateur.warhammer.base.exception.FunctionnalExeption;
import com.calculateur.warhammer.dao.verification.VerificationUtils;


/**
 * Template Method pour les DAO avec JPA
 * @author phili
 *
 * @param &lt;I&gt; Type de l'id
 * @param &lt;E&gt; Type de l'entité
 */
public abstract class AbstractDAO&lt;I,E extends IEntity&lt;I&gt;&gt; implements IDAO&lt;I, E&gt;{

	@PersistenceContext
	protected EntityManager em;
	
	@Inject
	@Named(&quot;transactionManager&quot;)
	protected PlatformTransactionManager transactionManager;
	
	/**
	 * 
	 * @return La classe sous forme de String pour les Query JPQL
	 */
	protected abstract String getEntityClass();
	
	/**
	 * 
	 * @return Le libellé de l'entité pour les query JPQL
	 */
	protected abstract String getEntityLibelle();
	
	/**
	 * 
	 * @return Le resource Bundle d'erreur pour les erreurs fonctionnelles
	 */
	protected abstract String getErrorBundle();
	
	/**
	 * 
	 * @return L'entité sous forme de classe
	 */
	protected abstract Class&lt;E&gt; getClassEntity();
	
	@Override
	public E save(E entity) throws DAOException, FunctionnalExeption {
		VerificationUtils.verifie(entity, getErrorBundle());
		TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
		try {
			em.persist(entity);
			transactionManager.commit(status);
			return entity;
		}catch (Exception e) {
			transactionManager.rollback(status);
			throw new DAOException(e);
		}
	}
	
	@Override
	public E update(E entity) throws DAOException, FunctionnalExeption {
		VerificationUtils.verifie(entity, getErrorBundle());
		TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
		try {
			E rEntity = em.merge(entity);
			transactionManager.commit(status);
			return rEntity;
		}catch (Exception e) {
			transactionManager.rollback(status);
			throw new DAOException(e);
		}
	}
	
	@Override
	public E getById(I id) throws DAOException {
		try {
			return em.find(getClassEntity(), id);
		}catch(Exception e) {
			throw new DAOException(e);
		}
	}
	
	@SuppressWarnings(&quot;unchecked&quot;)
	@Override
	public List&lt;E&gt; getAll() throws DAOException {
		try {
			Query query = em.createQuery(getJpqlAll());
			return query.getResultList();
		}catch(Exception e) {
			throw new DAOException(e);
		}
	}
	
	/**
	 * 
	 * @return Le JPQL pour avoir toutes les entités
	 */
	private String getJpqlAll() {
		StringBuilder sb = new StringBuilder(&quot;SELECT &quot;);
		sb.append(getEntityLibelle());
		sb.append(&quot; FROM &quot;);
		sb.append(getEntityClass());
		sb.append(&quot; &quot;);
		sb.append(getEntityLibelle());
		return sb.toString();
	}
	
	@Override
	public void delete(I id) throws DAOException {
		TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
		try {
			Query queryDelete = getDeleteQuery(id);
			queryDelete.executeUpdate();
			transactionManager.commit(status);
		}catch(Exception e) {
			transactionManager.rollback(status);
			throw new DAOException(e);
		}
		
	}
	
	/**
	 * On laisse la possibilité de surcharger cette méthode au cas où l'id soit complexe (clé sur 2 colonnes par exemple).
	 * @param id L'id de l'entité à supprimer en BDD.
	 * @return La query pour effacer l'entité (le paramètrage est fait.
	 */
	protected Query getDeleteQuery(I id) {
		Query query = em.createQuery(getDeleteJpql());
		query.setParameter(&quot;id&quot;, id);
		return query;
	}
	
	/**
	 * On laisse la possibilité de surcharger cette méthode au cas où l'id soit complexe (clé sur 2 colonnes par exemple).
	 * @return JPQL pour suprimer une entité
	 */
	protected String getDeleteJpql() {
		StringBuilder sb = new StringBuilder(&quot;DELETE &quot;);
		sb.append(&quot; FROM &quot;);
		sb.append(getEntityClass());
		sb.append(&quot; &quot;);
		sb.append(getEntityLibelle());
		sb.append(&quot; WHERE &quot;);
		sb.append(getEntityLibelle());
		sb.append(&quot;.id = : id&quot;);
		return sb.toString();
	}
	
	@Override
	public Long count() throws DAOException {
		try {
			Query query = em.createQuery(getJpqlCount());
			return (Long) query.getSingleResult();
		}catch (Exception e) {
			throw new DAOException(e);
		}
	}
	
	/**
	 * 
	 * @return Le JPQL pour compter ce qui est en BDD.
	 */
	private String getJpqlCount() {
		StringBuilder sb = new StringBuilder(&quot;SELECT DISTINCT(COUNT(&quot;);
		sb.append(getEntityLibelle());
		sb.append(&quot;)) FROM &quot;);
		sb.append(getEntityClass());
		sb.append(&quot; &quot;);
		sb.append(getEntityLibelle());
		return sb.toString();
	}
}</pre></td></tr></table></pre>
</div>Notez que dans mon projet, j'utilise JPA. Je fais donc un Template Method avec JPA. Mais on pourrait le faire avec JDBC. Ou SAX/JAXB si on utilise une BDD avec des fichiers XML.<br />
<br />
Toute exception est catché et rejettée comme <i>DAOException</i>, comme spécifié dans mon contrat.<br />
<br />
J'ai aussi injecté (via CDI/Spring) un objet pour les transactions:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:84px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br /></div></td><td valign="top"><pre style="margin: 0">
@Inject
@Named(&quot;transactionManager&quot;)
protected PlatformTransactionManager transactionManager;</pre></td></tr></table></pre>
</div>On a donc le pattern suivant pour toutes transactions:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:156px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td valign="top"><pre style="margin: 0">
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
	em.persist(entity);
	transactionManager.commit(status);
	return entity;
}catch (Exception e) {
	transactionManager.rollback(status);
	throw new DAOException(e);
}</pre></td></tr></table></pre>
</div>Au début, on active la transaction:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());</pre></td></tr></table></pre>
</div>Si tout s'est bien passé, on &quot;Commit&quot;:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">
transactionManager.commit(status);</pre></td></tr></table></pre>
</div>En cas d'accident, qui correspond à une exception, on &quot;RollBack&quot; par:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:60px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="26"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br /></div></td><td valign="top"><pre style="margin: 0">
transactionManager.rollback(status);</pre></td></tr></table></pre>
</div>J'aurais pu utiliser @transactionnal ( <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html" target="_blank">https://docs.spring.io/spring-framew...sactional.html</a> ) mais j'aime bien voir les étapes, et je n'aime pas le masquage induit par @transactionnal. De plus, je veux maitriser l'exception jetée en cas d'erreur.<br />
<br />
Comme je fais beaucoup de JPQL, j'ai besoin sous forme de String du nom de la classe (<i>getEntityClass()</i>) et de son éventuel libellé (<i>getEntityLibelle()</i>).<br />
Pour retrouvé par identifiant, j'ai besoin du type de la classe entité (<i>getClassEntity()</i>).<br />
<br />
Comme mes erreurs fonctionnelles sont traduites, j'ai aussi besoin du ResourceBundle (<i>getErrorBundle()</i>).<br />
<br />
Tout ça, seule la classe fille le sait.<br />
<br />
<b>11) Cas 1: Les camps</b><br />
Dans mon projet (qui consiste à calculer des morts à Warhammer 40K, je rappelle), j'ai des camps (Imperium, Chaos, Xenos), soit:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.entity.entity;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.Length;

import com.calculateur.warhammer.base.entity.IEntity;

@Entity
@Table(name = &quot;CAMP&quot;)
public class CampEntity implements IEntity&lt;Integer&gt;{

	@Id
	@Column(name = &quot;ID_CAMP&quot;,nullable = false)
	@NotNull(message = &quot;id.null&quot;)
	private Integer id;
	
	@Column(name = &quot;NOM_CAMP&quot;,length = 50,nullable = false)
	@NotNull(message = &quot;nom.null&quot;)
	@NotEmpty(message = &quot;nom.empty&quot;)
	@Length(max = 50,message = &quot;nom.size&quot;)
	private String nom;
	
	@Column(name = &quot;CLE_TRADUCTION_CAMP&quot;,length = 50,nullable = false)
	@NotNull(message = &quot;cle.traduction.null&quot;)
	@NotEmpty(message = &quot;cle.traduction.empty&quot;)
	@Length(max = 50,message = &quot;cle.traduction.size&quot;)
	private String cleTraduction;
	
	@OneToMany(mappedBy = &quot;camp&quot;,fetch = FetchType.LAZY)
	private Set&lt;FactionEntity&gt; factions;
	
	@Override
	public Integer getId() {
		return id;
	}

	@Override
	public void setId(Integer id) {
		this.id = id;
	}
	
	public String getNom() {
		return nom;
	}
	
	public void setNom(String nom) {
		this.nom = nom;
	}
	
	public String getCleTraduction() {
		return cleTraduction;
	}

	public void setCleTraduction(String cleTraduction) {
		this.cleTraduction = cleTraduction;
	}
	
	public Set&lt;FactionEntity&gt; getFactions() {
		return factions;
	}
	
	public void setFactions(Set&lt;FactionEntity&gt; factions) {
		this.factions = factions;
	}
}</pre></td></tr></table></pre>
</div>Je ne fais rien de spécial avec les camps, donc la DAO devient:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.dao.dao;

import javax.inject.Named;

import com.calculateur.warhammer.entity.entity.CampEntity;

/**
 * DAO de CampEntity
 * @author phili
 *
 */
@Named(&quot;campDAO&quot;)
public class CampDAO extends AbstractDAO&lt;Integer, CampEntity&gt;{

	private static final String ENTITY_CLASS = &quot;CampEntity&quot;;
	
	private static final String ENTITY_LIBELLE = &quot;campEntity&quot;;
	
	private static final String RES = &quot;com.calculateur.warhammer.entity.camp.camp&quot;;
	
	@Override
	protected String getEntityClass() {
		return ENTITY_CLASS;
	}

	@Override
	protected String getEntityLibelle() {
		return ENTITY_LIBELLE;
	}

	@Override
	protected String getErrorBundle() {
		return RES;
	}

	@Override
	protected Class&lt;CampEntity&gt; getClassEntity() {
		return CampEntity.class;
	}

}</pre></td></tr></table></pre>
</div>Pour le test, on a:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.dao.dao;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import com.calculateur.warhammer.base.dao.AbstractDAOTest;
import com.calculateur.warhammer.base.dao.IDAO;
import com.calculateur.warhammer.dao.builder.CampEntityBuilder;
import com.calculateur.warhammer.dao.configuration.ConfigurationTestBDDH2;
import com.calculateur.warhammer.entity.entity.CampEntity;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {ConfigurationTestBDDH2.class})
public class CampDAOTest extends AbstractDAOTest&lt;Integer, CampEntity, IDAO&lt;Integer,CampEntity&gt;&gt;{
	
	private static final Integer ID_INSERTION = 100;
	private static final Integer ID_NOT_IN_DATABASE = 101;
	
	@Inject
	@Named(&quot;campDAO&quot;)
	private IDAO&lt;Integer, CampEntity&gt; campDAO;

	
	
	@Override
	protected IDAO&lt;Integer, CampEntity&gt; getDAO() {
		return campDAO;
	}

	@Override
	protected List&lt;CampEntity&gt; getErrorsEntities() {
		List&lt;CampEntity&gt; entities = new ArrayList&lt;&gt;();
		//Id
		entities.add(CampEntityBuilder.getInstance().addId(null).addNom(STRING_TEST).addCleTraduction(STRING_TEST).build());
		//Nom
		entities.add(CampEntityBuilder.getInstance().addId(ID_INSERTION).addNom(null).addCleTraduction(STRING_TEST).build());
		entities.add(CampEntityBuilder.getInstance().addId(ID_INSERTION).addNom(EMPTY_STRING).addCleTraduction(STRING_TEST).build());
		entities.add(CampEntityBuilder.getInstance().addId(ID_INSERTION).addNom(getToLongString(50)).addCleTraduction(STRING_TEST).build());
		//Clé de traduction
		entities.add(CampEntityBuilder.getInstance().addId(ID_INSERTION).addNom(STRING_TEST).addCleTraduction(null).build());
		entities.add(CampEntityBuilder.getInstance().addId(ID_INSERTION).addNom(STRING_TEST).addCleTraduction(EMPTY_STRING).build());
		entities.add(CampEntityBuilder.getInstance().addId(ID_INSERTION).addNom(STRING_TEST).addCleTraduction(getToLongString(50)).build());
		return entities;
	}

	@Override
	protected Integer getIdWithNothingInDatabase() {
		return ID_NOT_IN_DATABASE;
	}

	@Override
	protected CampEntity getEntityForInsertion() {
		return CampEntityBuilder.getInstance().addId(ID_INSERTION).addNom(STRING_TEST).addCleTraduction(STRING_TEST).build();
	}

	@Override
	protected void compareEntities(CampEntity base, CampEntity aComparer) {
		Assertions.assertThat(aComparer.getId()).isEqualTo(base.getId());
		
	}

	@Override
	protected void updateEntity(CampEntity toUpdate) {
		toUpdate.setNom(STRING_TEST2);
		toUpdate.setCleTraduction(STRING_TEST2);
		
	}

	@Override
	protected void assertUpdate(CampEntity anEntity) {
		Assertions.assertThat(anEntity.getNom()).isEqualTo(STRING_TEST2);
		Assertions.assertThat(anEntity.getCleTraduction()).isEqualTo(STRING_TEST2);
	}

	@Override
	protected void testSupplementaires(CampEntity entityTest) {
		//Rien
	}

}</pre></td></tr></table></pre>
</div>J'utilise Spring et une BDD simple: H2 ( <a href="https://www.h2database.com/html/main.html" target="_blank">https://www.h2database.com/html/main.html</a> ).<br />
Notez que j'ai injecté (via Spring) la BDD et que j'ai injecté l'interface. Le test voit l'interface, pas la classe. Le test ne voit donc pas l'implémentation de la DAO.<br />
<br />
<b>12) Cas 2: Les factions</b><br />
A Warhammer 40K, j'ai aussi des factions (Space-marine, ...).<br />
Soit:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.entity.entity;

import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import com.calculateur.warhammer.base.entity.IEntity;
import com.calculateur.warhammer.entity.pk.FactionPK;

/**
 * Entité pour les faction en BDD
 * 
 * @author phili
 *
 */
@Entity
@Table(name = &quot;FACTION&quot;)
public class FactionEntity implements IEntity&lt;FactionPK&gt; {

	@EmbeddedId
	@NotNull(message = &quot;faction.pk.null&quot;)
	private FactionPK id;

	@Column(name = &quot;NOM&quot;,nullable = false,length = 50)
	@NotNull(message=&quot;faction.nom.null&quot;)
	@NotEmpty(message=&quot;faction.nom.empty&quot;)
	@Size(max = 50, message = &quot;faction.nom.size&quot;)
	private String nom;

	@Column(name = &quot;CLE_TRADUCTION&quot;,nullable = false,length = 50)
	@NotNull(message=&quot;faction.cle.traduction.null&quot;)
	@NotEmpty(message=&quot;faction.cle.traduction.empty&quot;)
	@Size(max = 50, message = &quot;faction.cle.traduction.size&quot;)
	private String cleTraduction;

	@ManyToOne(fetch = FetchType.EAGER)
	@JoinColumn(name = &quot;ID_CAMP&quot;,foreignKey = @ForeignKey(name = &quot;FK_CAMP&quot;),nullable = false,updatable = false,insertable = false)
	@NotNull(message = &quot;faction.camp.null&quot;)
	private CampEntity camp;
	
	@Override
	public FactionPK getId() {
		return id;
	}

	@Override
	public void setId(FactionPK id) {
		this.id = id;
	}

	public String getNom() {
		return nom;
	}

	public void setNom(String nom) {
		this.nom = nom;
	}

	public String getCleTraduction() {
		return cleTraduction;
	}

	public void setCleTraduction(String cleTraduction) {
		this.cleTraduction = cleTraduction;
	}

	public CampEntity getCamp() {
		return camp;
	}

	public void setCamp(CampEntity camp) {
		this.camp = camp;
	}
}</pre></td></tr></table></pre>
</div>Là, je vais avoir besoin de rechercher les camps par faction (les &quot;autres opérations d'une DAO&quot;).<br />
<br />
Je vais donc étendre la DAO de base:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.dao.contrat;

import java.util.List;

import com.calculateur.warhammer.base.dao.IDAO;
import com.calculateur.warhammer.base.exception.DAOException;
import com.calculateur.warhammer.entity.entity.FactionEntity;
import com.calculateur.warhammer.entity.pk.FactionPK;

/**
 * DAO spéciale pour les factions
 * @author phili
 *
 */
public interface IFactionDAO extends IDAO&lt;FactionPK, FactionEntity&gt;{

	/**
	 * 
	 * @param idCamp Id du camp
	 * @return La liste des factions pour l'id du camp
	 * @throws DAOException Si erreur lors de la recherche
	 */
	List&lt;FactionEntity&gt; getFactions(Integer idCamp)throws DAOException;
}</pre></td></tr></table></pre>
</div>Notez qu'ici, j'ai encore une interface, injectable dans diverses classes variées.<br />
<br />
On a l'implémentations suivante, aidée du Template Method:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.dao.dao;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.Query;

import com.calculateur.warhammer.base.dao.IDAO;
import com.calculateur.warhammer.base.exception.DAOException;
import com.calculateur.warhammer.base.exception.FunctionnalExeption;
import com.calculateur.warhammer.dao.contrat.IFactionDAO;
import com.calculateur.warhammer.dao.verification.VerificationUtils;
import com.calculateur.warhammer.entity.entity.CampEntity;
import com.calculateur.warhammer.entity.entity.FactionEntity;
import com.calculateur.warhammer.entity.pk.FactionPK;

@Named(&quot;factionDAO&quot;)
public class FactionDAO extends AbstractDAO&lt;FactionPK, FactionEntity&gt; implements IFactionDAO {

	private static final String ENTITY_CLASS = &quot;FactionEntity&quot;;

	private static final String ENTITY_LIBELLE = &quot;factionEntity&quot;;

	private static final String RES = &quot;com.calculateur.warhammer.entity.faction.faction&quot;;

	private static final String JPQL_DELETE = &quot;DELETE FROM FactionEntity factionEntity WHERE factionEntity.id.idCamp = :idCamp AND factionEntity.id.idFaction = :idFaction&quot;;

	private static final String JPQL_SELECT_BY_CAMP = &quot;SELECT DISTINCT(factionEntity) FROM FactionEntity factionEntity WHERE factionEntity.id.idCamp = :idCamp&quot;;

	@Inject
	@Named(&quot;campDAO&quot;)
	private IDAO&lt;Integer, CampEntity&gt; campDAO;

	@SuppressWarnings(&quot;unchecked&quot;)
	@Override
	public List&lt;FactionEntity&gt; getFactions(Integer idCamp) throws DAOException {
		try {
			Query query = em.createQuery(JPQL_SELECT_BY_CAMP);
			query.setParameter(&quot;idCamp&quot;, idCamp);
			return query.getResultList();
		} catch (Exception e) {
			throw new DAOException(e);
		}
	}

	@Override
	protected String getEntityClass() {
		return ENTITY_CLASS;
	}

	@Override
	protected String getEntityLibelle() {
		return ENTITY_LIBELLE;
	}

	@Override
	protected String getErrorBundle() {
		return RES;
	}

	@Override
	protected Class&lt;FactionEntity&gt; getClassEntity() {
		return FactionEntity.class;
	}

	// Surcharge à cause de l'id
	@Override
	protected Query getDeleteQuery(FactionPK id) {
		Query query = em.createQuery(JPQL_DELETE);
		query.setParameter(&quot;idCamp&quot;, id.getIdCamp());
		query.setParameter(&quot;idFaction&quot;, id.getIdFaction());
		return query;
	}

	@Override
	public FactionEntity save(FactionEntity entity) throws DAOException, FunctionnalExeption {
		verificationSupplementaire(entity);
		return super.save(entity);
	}

	@Override
	public FactionEntity update(FactionEntity entity) throws DAOException, FunctionnalExeption {
		verificationSupplementaire(entity);
		return super.update(entity);
	}

	/**
	 * Vérifications supplémentaires sur la clé primaire et le camp.
	 * 
	 * @param entity Entité à vérifier
	 * @throws FunctionnalExeption Si erreur fonctionnelle détectée
	 * @throws DAOException        Si erreur avec la BDD
	 */
	private void verificationSupplementaire(FactionEntity entity) throws FunctionnalExeption, DAOException {
		List&lt;String&gt; errors = new ArrayList&lt;&gt;();
		// Ce ne sera pas vérifié directement, il faut ruser
		if (entity.getId() != null) {
			VerificationUtils.verifie(entity.getId(), RES);
		}
		if (entity.getId() != null &amp;&amp; entity.getId().getIdCamp() != null &amp;&amp; entity.getCamp() != null) {
			verificationCoherenceCamp(entity, entity.getId(), errors);
		}
		if (!errors.isEmpty()) {
			throw new FunctionnalExeption(errors, RES);
		}
	}

	private void verificationCoherenceCamp(FactionEntity entity, FactionPK id, List&lt;String&gt; errors)
			throws DAOException {
		if (entity.getCamp().getId() == null) {
			errors.add(&quot;faction.camp.id.null&quot;);
		} else {
			verificationCoherenceCleComposee(entity, id, errors);
		}
	}

	private void verificationCoherenceCleComposee(FactionEntity entity, FactionPK id, List&lt;String&gt; errors)
			throws DAOException {
		if (id.getIdCamp().intValue() != entity.getCamp().getId().intValue()) {
			errors.add(&quot;faction.camp.id.error&quot;);
		}
		CampEntity aCamp = campDAO.getById(entity.getCamp().getId());
		if (aCamp == null) {
			errors.add(&quot;faction.camp.camp.null&quot;);
		}
	}		
}</pre></td></tr></table></pre>
</div>Notez qu'ici, j'ai une clé composite, ce qui fait que je surcharge le JPQL pour supprimer. Mais ça reste transparent pour l'utilisateur.<br />
<br />
Le TU devient:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.dao.dao;

import static org.junit.jupiter.api.Assertions.fail;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.inject.Named;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import com.calculateur.warhammer.base.dao.AbstractDAOTest;
import com.calculateur.warhammer.base.dao.IDAO;
import com.calculateur.warhammer.base.exception.DAOException;
import com.calculateur.warhammer.dao.builder.CampEntityBuilder;
import com.calculateur.warhammer.dao.builder.FactionEntityBuilder;
import com.calculateur.warhammer.dao.builder.pk.FactionPKBuilder;
import com.calculateur.warhammer.dao.configuration.ConfigurationTestBDDH2;
import com.calculateur.warhammer.dao.contrat.IFactionDAO;
import com.calculateur.warhammer.entity.entity.CampEntity;
import com.calculateur.warhammer.entity.entity.FactionEntity;
import com.calculateur.warhammer.entity.pk.FactionPK;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {ConfigurationTestBDDH2.class})
public class FactionDAOTest extends AbstractDAOTest&lt;FactionPK, FactionEntity, IFactionDAO&gt;{

	private static final Integer ID_CAMP_IMPERIUM = 1;
	private static final Integer ID_CAMP_CHAOS = 2;
	
	private static final Integer ID_NON_UTILISE = 1000;
	private static final Integer ID_TEST = 8;
	
	@Inject
	@Named(&quot;factionDAO&quot;)
	private IFactionDAO factionDAO;
	
	@Inject
	@Named(&quot;campDAO&quot;)
	private IDAO&lt;Integer, CampEntity&gt; campDAO;
	
	private CampEntity campImperium;
	
	@BeforeEach
	public void beforeTest() throws DAOException{
		campImperium = campDAO.getById(ID_CAMP_IMPERIUM);
	}
	
	@Override
	protected IFactionDAO getDAO() {
		return factionDAO;
	}

	@Override
	protected List&lt;FactionEntity&gt; getErrorsEntities() {
		List&lt;FactionEntity&gt; list = new ArrayList&lt;&gt;();
		//Id
		FactionEntityBuilder builder = FactionEntityBuilder.getInstance().addNom(STRING_TEST).addCleTraduction(STRING_TEST).addCamp(campImperium);
		list.add(builder.addId(null).build());
		list.add(builder.addId(FactionPKBuilder.getInstance().addIdCamp(null).addIdFaction(ID_TEST).build()).build());
		list.add(builder.addId(FactionPKBuilder.getInstance().addIdCamp(ID_CAMP_IMPERIUM).addIdFaction(null).build()).build());
		
		FactionPK id = FactionPKBuilder.getInstance().addIdCamp(ID_CAMP_IMPERIUM).addIdFaction(ID_TEST).build();
		//Nom
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(null).addCleTraduction(STRING_TEST).addCamp(campImperium).build());
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(EMPTY_STRING).addCleTraduction(STRING_TEST).addCamp(campImperium).build());
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(getToLongString(50)).addCleTraduction(STRING_TEST).addCamp(campImperium).build());
		//Cle traduction
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(STRING_TEST).addCleTraduction(null).addCamp(campImperium).build());
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(STRING_TEST).addCleTraduction(EMPTY_STRING).addCamp(campImperium).build());
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(STRING_TEST).addCleTraduction(getToLongString(50)).addCamp(campImperium).build());
		//Camp
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(STRING_TEST).addCleTraduction(STRING_TEST).addCamp(null).build());
		CampEntity aCamp = CampEntityBuilder.getInstance().addId(null).addNom(STRING_TEST).addCleTraduction(STRING_TEST).build();
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(STRING_TEST).addCleTraduction(STRING_TEST).addCamp(aCamp).build());
		aCamp = CampEntityBuilder.getInstance().addId(ID_CAMP_CHAOS).addNom(STRING_TEST).addCleTraduction(STRING_TEST).build();
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(STRING_TEST).addCleTraduction(STRING_TEST).addCamp(aCamp).build());
		id = FactionPKBuilder.getInstance().addIdCamp(ID_NON_UTILISE).addIdFaction(ID_TEST).build();
		aCamp = CampEntityBuilder.getInstance().addId(ID_NON_UTILISE).addNom(STRING_TEST).addCleTraduction(STRING_TEST).build();
		list.add(FactionEntityBuilder.getInstance().addId(id).addNom(STRING_TEST).addCleTraduction(STRING_TEST).addCamp(aCamp).build());
		return list;
	}

	@Override
	protected FactionPK getIdWithNothingInDatabase() {
		return FactionPKBuilder.getInstance().addIdCamp(ID_CAMP_IMPERIUM).addIdFaction(ID_NON_UTILISE).build();
	}

	@Override
	protected FactionEntity getEntityForInsertion() {
		FactionPK id = FactionPKBuilder.getInstance().addIdCamp(ID_CAMP_IMPERIUM).addIdFaction(ID_TEST).build();
		return FactionEntityBuilder.getInstance().addId(id).addNom(STRING_TEST).addCleTraduction(STRING_TEST).addCamp(campImperium).build();
	}

	@Override
	protected void compareEntities(FactionEntity base, FactionEntity aComparer) {
		Assertions.assertThat(base.getId().getIdCamp()).isEqualTo(aComparer.getId().getIdCamp());
		Assertions.assertThat(base.getId().getIdFaction()).isEqualTo(aComparer.getId().getIdFaction());		
	}

	@Override
	protected void updateEntity(FactionEntity toUpdate) {
		toUpdate.setNom(STRING_TEST2);
		toUpdate.setCleTraduction(STRING_TEST2);
	}

	@Override
	protected void assertUpdate(FactionEntity anEntity) {
		Assertions.assertThat(anEntity.getNom()).isEqualTo(STRING_TEST2);
		Assertions.assertThat(anEntity.getCleTraduction()).isEqualTo(STRING_TEST2);
	}

	@Override
	protected void testSupplementaires(FactionEntity entityTest) {
		try {
			List&lt;FactionEntity&gt; list = factionDAO.getFactions(ID_CAMP_IMPERIUM);
			List&lt;FactionEntity&gt; aList = list.stream().filter(f -&gt; f.getId().getIdFaction().equals(ID_TEST)).map(f -&gt; f).collect(Collectors.toList());
			Assertions.assertThat(aList).isNotEmpty();
			Assertions.assertThat(aList).hasSize(1);
		} catch (DAOException e) {
			fail(e);
		}
	}

}</pre></td></tr></table></pre>
</div><b>Bibliographie</b><br />
<a href="https://www.baeldung.com/java-dao-pattern" target="_blank">https://www.baeldung.com/java-dao-pattern</a><br />
<a href="https://openclassrooms.com/fr/courses/4504771-simplifiez-le-developpement-dapplications-java-avec-spring/4758254-implementer-des-dao" target="_blank">https://openclassrooms.com/fr/course...menter-des-dao</a><br />
<a href="https://www.web-dev-qa-db-fra.com/fr/java/objet-dacces-aux-donnees-dao-en-java/1041827090/" target="_blank">https://www.web-dev-qa-db-fra.com/fr...va/1041827090/</a><br />
<a href="https://www.digitalocean.com/community/tutorials/dao-design-pattern" target="_blank">https://www.digitalocean.com/communi...design-pattern</a></blockquote>

]]></content:encoded>
			<dc:creator>PhilippeGibault</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/280180-philippegibault/b10370/parlons-dao/</guid>
		</item>
		<item>
			<title>Comment gérer les règles de Warhammer 40K avec les Design Pattern</title>
			<link>https://www.developpez.net/forums/blogs/280180-philippegibault/b10354/gerer-regles-warhammer-40k-design-pattern/</link>
			<pubDate>Sun, 10 Jul 2022 12:11:11 GMT</pubDate>
			<description><![CDATA[C'est connu, il y a trop de...]]></description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">C'est connu, il y a trop de règles à Warhammer 40K;<br />
<br />
C'est d'ailleurs ce qui rebute.<br />
<br />
Mais les règles deviennent importantes si on espère faire un calcul de probabilité.<br />
<br />
Heureusement, il existe les Designs patterns qui vont permettre d'un peu simplifier le problème, et passer à un problème bourratif d'implémentation de règles.<br />
<br />
Déjà, commençons par un point: Une figurine n'a pas de règles (excepté celles de base).<br />
<br />
Si on prend un space marine, il a les règles de spaces-marines (Et il ne connaitrons pas la peur, les doctrines...).<br />
<br />
Les règles du space marine s'ajoute aux règles de bases (aucune à la base, je précise, ça va avoir son importance).<br />
<br />
Et puis il y a les chapitres. Comme par exemple les Dark Angels.<br />
<br />
Les règles des Dark Angels (selon qu'il soit de la Deathwing, de la Ravenwing ou ni l'un, ni l'autre) qui s'ajoute aux règles de marines qui s'ajoute aux règles &quot;de bases&quot;.<br />
<br />
<u>On remarque donc qu'on a une notion d'héritage</u><br />
<br />
Ceci étant fait, il va falloir faire un point sur les Designs Patterns.<br />
<br />
<b>Les Designs Patterns</b><br />
Il y a plusieurs approches.<br />
<br />
La plus connue, dont on va parler est le <a href="https://fr.wikipedia.org/wiki/Design_Patterns" target="_blank">GOF</a>.<br />
<br />
Effectivement,  Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides (Gang of Four, gang des 4) ont écrit un livre de référence sur les patrons architecturaux en langage objet. C'est de ça que l'on va parler.<br />
<br />
A noter, que pour la théorie, il y a le <a href="https://fr.wikipedia.org/wiki/GRASP_(programmation)" target="_blank">GRAPS</a> Et dans la vie du codeur de base, il y a <a href="https://fr.wikipedia.org/wiki/SOLID_(informatique)" target="_blank">SOLID</a>.<br />
<br />
SOLID est la philosophie qui doit guider le développement au quotidien. Mais on va voir que GOF et SOLID, si au premier abord n'ont pas la même approche, sont en fait complémentaires.<br />
<br />
<b>Partons d'un principe de base</b><br />
On va faire plus simple. Je pense qu'il y a un principe de base pour faire une bonne architecture.<br />
<br />
&quot;Je sais ce que tu fais, mais je n'ai pas envie de savoir comment tu es codé&quot;.<br />
<br />
Effectivement, le programme qui va calculer les proba a juste besoin des règles (et proflls). Il a besoin de ce que l'on appelle en UML l'interface, c'est à dire la signature des méthodes.<br />
Il n'a pas besoin de savoir comment c'est implémenté.<br />
<br />
C'est d'ailleurs pour ça que dans <a href="https://www.amazon.fr/Effective-Java-Joshua-Bloch/dp/0134685997" target="_blank">Effective Java</a> Joshua Bloch explique qu'il faut préférer les classes abstraites aux interfaces.<br />
<br />
Ce principe est identique au modèle Réseau <a href="https://fr.wikipedia.org/wiki/Mod%C3%A8le_OSI" target="_blank">OSI</a>.<br />
<br />
Ce qui tombe bien, l'Interface, notion en particulier UML a une réalité en Java. C'est un type.<br />
<br />
<b>Premier contrat, l'identifiable</b><br />
On va donc définir nos interfaces, que je vais nommer contrat.<br />
Et comme dans warhammer 40K tout le monde a sa petite règle (la figurine, le bâtiment,le pouvoir psychique et l'aura...), on va donc définir l'identifiable:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.identifiable;

/**
 * Cette interface permet de contractualiser un identifiable. Celui-ci est identifié par un id entier
 * @author phili
 *
 */
public interface IIdentifiable {

	/**
	 * 
	 * @return L'id de l'identifiable
	 */
	Integer getId();
}</pre></td></tr></table></pre>
</div>L'identifiable est identifiable par son... identifiant. Ce qui pourra être utilse quand on le mettra en Base de données (où néanmoins, avec Hibernate, on préfère les Long).<br />
<br />
Deux identifiables vont être important pour le calcul:<br />
<br />
Le profil:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.identifiable;

import java.util.Set;

import com.calculateur.warhammer.data.enumeration.EProfil;

/**
 * Un profil à warhammer 40K
 * @author phili
 *
 */
public interface IProfil extends IIdentifiable{

	/**
	 * 
	 * @return Le mouvelment
	 */
	Integer getMouvement();
	
	/**
	 * 
	 * @return La CC
	 */
	Integer getCapaciteDeCombat();
	
	/**
	 * 
	 * @return La CT
	 */
	Integer getCapaciteTir();
	
	/**
	 * 
	 * @return La Force
	 */
	Integer getForce();
	
	/**
	 * 
	 * @return L'endurance
	 */
	Integer getEndurance();
	
	/**
	 * 
	 * @return Le nombre de point de vie
	 */
	Integer getNombrePointDeVie();
	
	/**
	 * 
	 * @return Le nombre d'attaque de base
	 */
	Integer getNombreAttaque();
	
	/**
	 * 
	 * @return Le nombre d'attaque par D3
	 */
	Integer getNombreAttaqueParD3();
	
	/**
	 * 
	 * @return Le nombre d'attaque par D6
	 */
	Integer getNombreAttaqueParD6();
	
	/**
	 * 
	 * @return Le commandement
	 */
	Integer getCommandement();
	
	/**
	 * 
	 * @return La sauvegarde
	 */
	Integer getSauvegarde();
	
	/**
	 * 
	 * @return La sauvegarde Invulnérable
	 */
	Integer getSauvegardeInvulnerable();
	
	/**
	 * 
	 * @return Les types du profil
	 */
	Set&lt;EProfil&gt; getTypesProfils();
	
	/**
	 * 
	 * @return Vrai si c'est un volant
	 */
	Boolean isVolant();
}</pre></td></tr></table></pre>
</div>Le bâtiment:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.identifiable;

/**
 * Cette interface représente un batiment à 40K
 * @author phili
 *
 */
public interface IBatiment extends IIdentifiable{

	/**
	 * 
	 * @return Vrai si le batiment est identifiable
	 */
	boolean isDefendable();
	
	/**
	 * 
	 * @return Vrai si le batiment est un couvert dense
	 */
	boolean isCouvertDense();
	
	/**
	 * 
	 * @return Vrai si le batiment est un couvert léger
	 */
	boolean isCouvertLeger();
	
	/**
	 * 
	 * @return Vrai si le batiment est un couvert lourd
	 */
	boolean isCouvertLourd();
	
	/**
	 * 
	 * @return Vrai si bâtiment est exaltant
	 */
	boolean isExaltant();
}</pre></td></tr></table></pre>
</div>Ainsi que les armes:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.identifiable;

/**
 * L'arme utilisé
 * @author phili
 *
 */
public interface IArme extends IIdentifiable{

	/**
	 * 
	 * @return La PA de l'arme
	 */
	Integer getPA();
	
	/**
	 * 
	 * @return Le nombre de D3 qu'il faut lancer pour avoir la PA
	 */
	Integer getPAParD3();
	
	/**
	 * 
	 * @return Le nomnbre de D6 qu'il faut lancer pour avoir la PA
	 */
	Integer getPAParD6();
	
	/**
	 * 
	 * @return Les dégats de base
	 */
	Integer getDegâts();
	
	/**
	 * 
	 * @return Le nombre de D3 qu'il faut avoir pour avoir les dégâts
	 */
	Integer getDegatsParD3();
	
	/**
	 * 
	 * @return Nombre d'attaque par D6
	 */
	Integer getDegatsParD6();
}</pre></td></tr></table></pre>
</div>Notez qu'il existe des armes de corps à corps et de tir.<br />
Dans le contexte des interfaces Java, on va composer.<br />
<br />
On introduit donc les armes de tir:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.identifiable;

import com.calculateur.warhammer.data.enumeration.EArmeTir;

/**
 * Les spécifications pour l'arme de tir
 * @author phili
 *
 */
public interface IArmeDeTir extends IArme{

	/**
	 * 
	 * @return Portée minimale de l'arme
	 */
	Integer getPorteeMinimale();
	
	/**
	 * 
	 * @return Portée maximale de l'arme
	 */
	Integer getPorteeMaximale();
	
	/**
	 * 
	 * @return Portée maximale de l'arme
	 */
	EArmeTir getType();
	
	/**
	 * 
	 * @return Nombre d'attaque de base
	 */
	Integer getNombreAttaqueBase();
	
	/**
	 * 
	 * @return Le nombre de D3 qu'il faut lancer pour attaque
	 */
	Integer getNombreAttaqueD3();
	
	/**
	 * 
	 * @return Le nombre de D6 qu'il faut lancer pour une attaque
	 */
	Integer getNombreAttaqueD6();
	
	/**
	 * 
	 * @return le nombre minimum qu'il faut lancer pour un daka
	 */
	Integer getNombreDakaMin();
	
	/**
	 * 
	 * @return le nombre maximum qu'il faut lancer pour un Daka
	 */
	Integer getNombreDakaMax();
	
	/**
	 * 
	 * @return Vrai si il faut faire un jet de touche
	 */
	Boolean isJetTouche();
	
	/**
	 * 
	 * @return Vrai si l'arme est Blast
	 */
	Boolean isBlast();
	
	/**
	 * 
	 * @return Vrai si l'arme peut tirer sans ligne de vue
	 */
	boolean isTirIndirect();
}</pre></td></tr></table></pre>
</div>Et l'arme de corps à corps:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.identifiable;

/**
 * Cette Interface représente les armes de corps à corps
 * @author phili
 *
 */
public interface IArmeDeCorpsACorps extends IArme{

	/**
	 * 
	 * @return Bonus d'attaques aditionnel
	 */
	Integer getBonusAttaqueFixe();
	
	/**
	 * 
	 * @return Bonus aditionnel donné par un nombre de D3
	 */
	Integer getBonusAttaqueD3();
	
	/**
	 * 
	 * @return Bonus aditionnel donné par un nombre de D6
	 */
	Integer getBonusAttaqueD6();
	
	/**
	 * 
	 * @return Bonus Additionnel en force donné par l'arme (F + bonus)
	 */
	Integer getBonusAditionForce();
	
	/**
	 * 
	 * @return Bonus multiplicatif donné par l'arme Bonus * (F + bonus aditionnel)
	 */
	Integer getBonusMultiplicationForce();
	
	/**
	 * 
	 * @return Bonus Additionnel en force donné par l'arme après avoir jeté un vombre de D3
	 */
	Integer getBonusAditionForceD3();
	
	/**
	 * 
	 * @return Bonus Additionnel en force donné par l'arme après avoir jeté un vombre de D6
	 */
	Integer getBonusAditionForceD6();
}</pre></td></tr></table></pre>
</div><b>Contrat pour les règles</b><br />
On va donc définir un contrat pour les règles.<br />
Pour commencer, une règles est pour un identifiable. Notez que contrairement à ce que j'ai rendu pour le SMB116, on traite les bâtiments comme ayant des règles.<br />
On a donc la règle, avec son identifiable (pour la retrouver, bien évidement).<br />
<br />
<div class="bbcode_container">
	<div class="bbcode_quote">
		<div class="quote_container">
			<div class="bbcode_quote_container"></div>
			
				package com.calculateur.warhammer.data.regles;<br />
<br />
import com.calculateur.warhammer.data.identifiable.IIdentifiable;<br />
<br />
/**<br />
 * Règle spéciale pour un identifiable<br />
 * @author phili<br />
 *<br />
 * @param &lt;I&gt; Identifiable auquel s'applique la règle<br />
 */<br />
public interface IRegle &lt;I extends IIdentifiable&gt;{<br />
<br />
	/**<br />
	 * <br />
	 * @return L'identifiable à laquelle la règle s'applique<br />
	 */<br />
	I getIdentifiable();<br />
}
			
		</div>
	</div>
</div>La pratique a montré qu'il fallait décomposer les règles de l'attaquant et du défenseur, au moins pour limiter la taille des classes, et pour avoir une meilleur lisibilité du code.<br />
<br />
Donc on a:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br />143<br />144<br />145<br />146<br />147<br />148<br />149<br />150<br />151<br />152<br />153<br />154<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles;

import java.util.Map;
import java.util.Set;

/**
 * Règle d'attaque
 * @author phili
 */
public interface IRegleAttaque{

	//Règle mouvement
	/**
	 * 
	 * @return Vrai si celui-ci ignore les malus du mouvement d'avance au tir
	 */
	boolean isIgnoreMouvementAvanceAuTir();
	
	/**
	 * 
	 * @return Vrai si celui-ci ignore les malus du mouvement normal au tir
	 */
	boolean isIgnoreMouvementNormalAuTir();
	
	/**
	 * 
	 * @return Vrai si pour une arme à tir rapide, la figurine tir toujours 2 fois
	 */
	boolean isMaitriseArmeTirRapide();
	
	//Jet de touche
	/**
	 * 
	 * @return Vrai si le jet de touche a un bonus.
	 */
	Integer getBonusJetTouche();
	
	/**
	 * 
	 * @return Les scores relançables
	 */
	Set&lt;Integer&gt; getScoreJetToucheRelancable();
	
	/**
	 * 
	 * @return Jet minimum pour toucher
	 */
	Integer getJetMinimumPourToucher();
	
	/**
	 * 
	 * @return La map qui donne un jet de blessures mortelles, et on s'arrête après le jet de touche. &lt;br/&gt; 
	 * Le second chiffre donne le nombre de blessures mortelles
	 */
	Map&lt;Integer, Integer&gt; getMapJetToucheBlessuresMortelleEtOnArrete();
	
	/**
	 * 
	 * @return La Map de blessures Mortelles et on continue la séquence. C'est donc des blessures mortelles en plus.&lt;br/&gt;
	 * Le second chiffre est le nombre de blessures mortelles
	 */
	Map&lt;Integer,Integer&gt; getMapJetToucheBlessuresMortellesEtContinue();
	
	/**
	 * 
	 * @return La map score qui donne des attaques en plus. Le second chiffre est le nombre d'attaqye
	 */
	Map&lt;Integer,Integer&gt; getMapJetToucheAttaqueSupplementaire();
	
	//Force
	/**
	 * 
	 * @return Le bonus de force de l'adversaire
	 */
	Integer getBonusForce();
	
	//Endurance
	/**
	 * 
	 * @return Malus appliqué à une cible sur l'endurance
	 */
	Integer getMalusEndurance();
	
	//Jet de blessures
	/**
	 * 
	 * @return le bonus au jet de touche
	 */
	Integer getBonusJetBlessures();
	
	/**
	 * 
	 * @return Jet de blessures relançable
	 */
	Set&lt;Integer&gt; getScoreJetBlessureRelancable();
	
	/**
	 * 
	 * @return Jet minimum pour blesser
	 */
	Integer getJetMinumumPourBlesser();
	
	/**
	 * 
	 * @return La map qui donne un jet de blessures mortelles, et on s'arrête après le jet de blessure. &lt;br/&gt; 
	 * Le second chiffre donne le nombre de blessures mortelles
	 */
	Map&lt;Integer, Integer&gt; getMapJetBlessuresBlessuresMortelleEtOnArrete();
	
	/**
	 * 
	 * @return La Map de blessures Mortelles et on continue la séquence. C'est donc des blessures mortelles en plus.&lt;br/&gt;
	 * Le second chiffre est le nombre de blessures mortelles
	 */
	Map&lt;Integer,Integer&gt; getMapJetBlessuresBlessuresMortellesEtContinue();
	
	/**
	 * 
	 * @return La map qui pour un jet de touche, donne un bonus à la PA.&lt;br/&gt;
	 * Le second chiffre est le bonus à la PA
	 */
	Map&lt;Integer,Integer&gt; getMapJetBlessureBonusPA();
	
	//Attaques
	Integer getBonusNombreAttaque();
	
	//Commandement
	/**
	 * 
	 * @return Le malus que la règle donne aux test d'attritions
	 */
	Integer getMalusSurAttrition();
	
	//Sauvegarde
	/**
	 * 
	 * @return Vrai si l'attaquant ignore la sauvegarde invulnérable
	 */
	boolean isIgnoreSauvegardeInvulnerable();
	
	/**
	 * 
	 * @return Vrai si on fait ignorer la sauvegarde de couvert
	 */
	boolean isIgnoreSauvegardeCouvert();
	
	//Se relève
	/**
	 * 
	 * @return Vrai si la cible ne peut pas ignorer les blessures
	 */
	boolean isIgnoreIgnoreBlessure();
}</pre></td></tr></table></pre>
</div><div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles;

import java.util.Set;

import com.calculateur.warhammer.data.enumeration.EBlessure;

/**
 * Règle pour se défendre
 * @author phili
 *
 */
public interface IRegleDefense {

	//Jet de touche
	/**
	 * 
	 * @return Malus que subit l'attaquant au jet de touche
	 */
	Integer getMalusJetTouche();
	
	/**
	 * 
	 * @return Le jet minimum pour être touché
	 */
	Integer getJetMinimumPourEtreTouche();
	
	//Force
	/**
	 * 
	 * @return Malus que l'attaquant subit à la sauvegarde
	 */
	Integer getMalusForce();
	
	//Endurance
	/**
	 * 
	 * @return Bonus en endurance dont bénéficie le défenseur
	 */
	Integer getBonusEndurance();
	
	//Jet blessure
	/**
	 * 
	 * @return Jet minimum pour être blessé
	 */
	Integer getJetMinimumPourEtreBlesse();
	
	/**
	 * 
	 * @return Malus au jet de blessure
	 */
	Integer getMalusJetBlessure();
	
	//PV
	/**
	 * 
	 * @return Le nombre maximum de PV que la figurine peut perdre par phase
	 */
	Integer getNombrePointsViesMaximumPerdable();
	
	//Attaques
	/**
	 * 
	 * @return Malus que l'attaquant subit au nombre d'attaque.
	 */
	Integer getMalusNombreAttaque();
	
	//Sauvegarde
	/**
	 * 
	 * @return Vrai si il y a une sauvegarde de couvert
	 */
	boolean isSauvegardeCouvert();
	
	//Commandement
	/**
	 * 
	 * @return Vrai si la défense ignore le test de commandement
	 */
	boolean isIgnoreTestCommandement();
	
	/**
	 * 
	 * @return Bonus aux tests de commandement
	 */
	Integer getBonusCommandement();
	
	/**
	 * 
	 * @return Vrai si le test d'attrition est ignoré
	 */
	boolean isIgnoreTestAttrition();
	
	/**
	 * 
	 * @return Vrai si les malus au tests d'attrition sont ignorés
	 */
	boolean isIgnoreMalusAttrition();
	
	//Se relève
	/**
	 * 
	 * @return Le score pour se relever après la mort
	 */
	Integer getJetSeReleve();
	
	/**
	 * 
	 * @return Les relance du jet pour se relever
	 */
	Set&lt;Integer&gt; getRelancesSeReleve();
	
	//Ignore blessures
	/**
	 * 
	 * @param typeBlessures
	 * @return Le jet pour ignorer une blessure
	 */
	Integer getJetSoreIgnoreBlessure(EBlessure typeBlessures);
}</pre></td></tr></table></pre>
</div>A ce stade, on ne va pas implémenter pour chaque faction (il y en a trop) mais pour des cas généraux.<br />
<br />
<b>A la base, pas de règles: Design Pattern Template Method</b><br />
Comme dit précédemment,  à la base, on applique pas de règle spéciales, mais celle du livre de base.<br />
<br />
Ce qui permet de faire une implémentation pour la base, soit:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br />143<br /></div></td><td valign="top"><pre style="margin: 0">

package com.calculateur.warhammer.data.regles;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

import com.calculateur.warhammer.data.identifiable.IIdentifiable;

/**
 * Règles d'attaque de base pour un identifiable (c'est à dire aucune)
 * 
 * @author phili
 *
 */
public class RegleAttaqueBase&lt;I extends IIdentifiable&gt; implements IRegle&lt;I&gt;, IRegleAttaque {

	private static final Integer NO_BONUS = 0;

	private static final Integer NO_JET_MINIMUM = 0;

	private final I identifiable;

	public RegleAttaqueBase(I identifiable) {
		this.identifiable = identifiable;
	}

	@Override
	public boolean isIgnoreMouvementAvanceAuTir() {
		return false;
	}

	@Override
	public boolean isIgnoreMouvementNormalAuTir() {
		return false;
	}

	@Override
	public boolean isMaitriseArmeTirRapide() {
		return false;
	}

	@Override
	public Integer getBonusJetTouche() {
		return NO_BONUS;
	}

	@Override
	public Set&lt;Integer&gt; getScoreJetToucheRelancable() {
		return Collections.emptySet();
	}

	@Override
	public Integer getJetMinimumPourToucher() {
		return NO_JET_MINIMUM;
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetToucheBlessuresMortelleEtOnArrete() {
		return Collections.emptyMap();
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetToucheBlessuresMortellesEtContinue() {
		return Collections.emptyMap();
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetToucheAttaqueSupplementaire() {
		return Collections.emptyMap();
	}

	@Override
	public Integer getBonusForce() {
		return NO_BONUS;
	}

	@Override
	public Integer getMalusEndurance() {
		return NO_BONUS;
	}

	@Override
	public Integer getBonusJetBlessures() {
		return NO_BONUS;
	}

	@Override
	public Set&lt;Integer&gt; getScoreJetBlessureRelancable() {
		return Collections.emptySet();
	}

	@Override
	public Integer getJetMinumumPourBlesser() {
		return NO_JET_MINIMUM;
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetBlessuresBlessuresMortelleEtOnArrete() {
		return Collections.emptyMap();
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetBlessuresBlessuresMortellesEtContinue() {
		return Collections.emptyMap();
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetBlessureBonusPA() {
		return Collections.emptyMap();
	}

	@Override
	public Integer getBonusNombreAttaque() {
		return NO_BONUS;
	}

	@Override
	public Integer getMalusSurAttrition() {
		return NO_BONUS;
	}

	@Override
	public I getIdentifiable() {
		return identifiable;
	}

	@Override
	public boolean isIgnoreSauvegardeInvulnerable() {
		return false;
	}

	@Override
	public boolean isIgnoreSauvegardeCouvert() {
		return false;
	}

	@Override
	public boolean isIgnoreIgnoreBlessure() {
		return false;
	}
}</pre></td></tr></table></pre>
</div><div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles;

import java.util.Collections;
import java.util.Set;

import com.calculateur.warhammer.data.enumeration.EBlessure;
import com.calculateur.warhammer.data.identifiable.IIdentifiable;

public class RegleDefenseBase&lt;I extends IIdentifiable&gt; implements IRegle&lt;I&gt;,IRegleDefense  {

	private static final Integer NO_BONUS = 0;

	private static final Integer NO_JET_MINIMUM = 0;

	private final I identifiable;
	
	public RegleDefenseBase(I identifiable) {
		this.identifiable = identifiable;
	}
	
	@Override
	public Integer getMalusJetTouche() {
		return NO_BONUS;
	}

	@Override
	public Integer getJetMinimumPourEtreTouche() {
		return NO_JET_MINIMUM;
	}

	@Override
	public Integer getMalusForce() {
		return NO_BONUS;
	}

	@Override
	public Integer getBonusEndurance() {
		return NO_BONUS;
	}

	@Override
	public Integer getJetMinimumPourEtreBlesse() {
		return NO_JET_MINIMUM;
	}

	@Override
	public Integer getMalusJetBlessure() {
		return NO_BONUS;
	}

	@Override
	public Integer getNombrePointsViesMaximumPerdable() {
		return NO_BONUS;
	}

	@Override
	public Integer getMalusNombreAttaque() {
		return NO_BONUS;
	}

	@Override
	public boolean isSauvegardeCouvert() {
		return false;
	}

	@Override
	public boolean isIgnoreTestCommandement() {
		return false;
	}

	@Override
	public Integer getBonusCommandement() {
		return NO_BONUS;
	}

	@Override
	public boolean isIgnoreTestAttrition() {
		return false;
	}

	@Override
	public boolean isIgnoreMalusAttrition() {
		return false;
	}

	@Override
	public Integer getJetSeReleve() {
		return NO_BONUS;
	}

	@Override
	public Set&lt;Integer&gt; getRelancesSeReleve() {
		return Collections.emptySet();
	}

	@Override
	public Integer getJetSoreIgnoreBlessure(EBlessure typeBlessures) {
		return NO_JET_MINIMUM;
	}

	@Override
	public I getIdentifiable() {
		return identifiable;
	}

}</pre></td></tr></table></pre>
</div>On a donc de fait un Template Method, vu que les règles des Spaces Marines vont hériter, des règles de bases...<br />
<br />
En général, ce qui est implémenté dans un <a href="https://en.wikipedia.org/wiki/Template_method_pattern" target="_blank">Template Method</a> dépends de spécifications qui dépendent du contexte.<br />
<br />
C'est pour ça que ces &quot;spécificatons&quot; sont des méthodes abstraites. Le Template Méthode est donc une classe abstraite avec des méthodes abstraites pour pouvoir prendre en compte les spécifications qui seront dans les classes filles.<br />
<br />
Ce n'est pas le cas ici.<br />
<br />
Un autre problème, c'est qu'il risque dans certains cas, il risque d'y avoir beaucoup de classes filles.<br />
<br />
<b>Pour gérer l'ensemble des règles: Le Design Pattern Décorateur</b><br />
Dans un cas réél, on est un space marine, avec ses règles, dans un bâtiment qui a ses règles, avec des chefs, qui eux aussi donnent leurs règles...<br />
<br />
Pas de problème, il existe aussi un Design Pattern pour ça: le Design Pattern <a href="https://en.wikipedia.org/wiki/Decorator_pattern" target="_blank">Décorateur</a>.<br />
On va passer la liste des règles, et on au final, l'utilisateur ne va voir qu'une seule règle, celles à appliquer.<br />
<br />
Ici, la règle vue par l'utilisateur (donc celle à appliquer) est décorée par un ensemble de règle.<br />
<br />
Ce qui donne:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br />143<br />144<br />145<br />146<br />147<br />148<br />149<br />150<br />151<br />152<br />153<br />154<br />155<br />156<br />157<br />158<br />159<br />160<br />161<br />162<br />163<br />164<br />165<br />166<br />167<br />168<br />169<br />170<br />171<br />172<br />173<br />174<br />175<br />176<br />177<br />178<br />179<br />180<br />181<br />182<br />183<br />184<br />185<br />186<br />187<br />188<br />189<br />190<br />191<br />192<br />193<br />194<br />195<br />196<br />197<br />198<br />199<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Un décorateur qui prend un ensemble de règles d'attaque et fait un traitement
 * pour sortir la bonne règle au bon moment.
 * 
 * @author phili
 *
 */
public class RegleAttaqueDecorateur implements IRegleAttaque {

	private final List&lt;IRegleAttaque&gt; regles;

	public RegleAttaqueDecorateur(List&lt;IRegleAttaque&gt; regles) {
		this.regles = regles;
	}

	@Override
	public boolean isIgnoreMouvementAvanceAuTir() {
		return regles.stream().anyMatch(r -&gt; r.isIgnoreMouvementAvanceAuTir());
	}

	@Override
	public boolean isIgnoreMouvementNormalAuTir() {
		return regles.stream().anyMatch(r -&gt; r.isIgnoreMouvementNormalAuTir());
	}

	@Override
	public boolean isMaitriseArmeTirRapide() {
		return regles.stream().anyMatch(r -&gt; r.isMaitriseArmeTirRapide());
	}

	@Override
	public Integer getBonusJetTouche() {
		return regles.stream().mapToInt(r -&gt; r.getBonusJetTouche()).sum();
	}

	@Override
	public Set&lt;Integer&gt; getScoreJetToucheRelancable() {
		Set&lt;Integer&gt; sReturn = new HashSet&lt;&gt;();
		regles.stream().forEach(r -&gt; sReturn.addAll(r.getScoreJetToucheRelancable()));
		return sReturn;
	}

	@Override
	public Integer getJetMinimumPourToucher() {
		Integer result = 0;
		Integer aResult = regles.stream().filter(r -&gt; r.getJetMinimumPourToucher() &gt; 0)
				.mapToInt(r -&gt; r.getJetMinimumPourToucher()).min().getAsInt();
		return (aResult != null) ? aResult : result;
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetToucheBlessuresMortelleEtOnArrete() {
		Map&lt;Integer, Integer&gt; mReturn = new HashMap&lt;&gt;();
		Integer value;
		for (IRegleAttaque regle : regles) {
			for (Entry&lt;Integer, Integer&gt; entry : regle.getMapJetToucheBlessuresMortelleEtOnArrete().entrySet()) {
				value = mReturn.computeIfAbsent(entry.getKey(), k -&gt; 0);
				value += entry.getValue();
				mReturn.put(entry.getKey(), value);
			}
		}
		return mReturn;
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetToucheBlessuresMortellesEtContinue() {
		Map&lt;Integer, Integer&gt; mReturn = new HashMap&lt;&gt;();
		Integer value;
		for (IRegleAttaque regle : regles) {
			for (Entry&lt;Integer, Integer&gt; entry : regle.getMapJetToucheBlessuresMortellesEtContinue().entrySet()) {
				value = mReturn.computeIfAbsent(entry.getKey(), k -&gt; 0);
				value += entry.getValue();
				mReturn.put(entry.getKey(), value);
			}
		}
		return mReturn;
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetToucheAttaqueSupplementaire() {
		Map&lt;Integer, Integer&gt; mReturn = new HashMap&lt;&gt;();
		Integer value;
		for (IRegleAttaque regle : regles) {
			for (Entry&lt;Integer, Integer&gt; entry : regle.getMapJetToucheAttaqueSupplementaire().entrySet()) {
				value = mReturn.computeIfAbsent(entry.getKey(), k -&gt; 0);
				value += entry.getValue();
				mReturn.put(entry.getKey(), value);
			}
		}
		return mReturn;
	}

	@Override
	public Integer getBonusForce() {
		return regles.stream().mapToInt(r -&gt; r.getBonusForce()).sum();
	}

	@Override
	public Integer getMalusEndurance() {
		return regles.stream().mapToInt(r -&gt; r.getMalusEndurance()).sum();
	}

	@Override
	public Integer getBonusJetBlessures() {
		return regles.stream().mapToInt(r -&gt; r.getBonusJetBlessures()).sum();
	}

	@Override
	public Set&lt;Integer&gt; getScoreJetBlessureRelancable() {
		Set&lt;Integer&gt; sReturn = new HashSet&lt;&gt;();
		regles.stream().forEach(r -&gt; sReturn.addAll(r.getScoreJetBlessureRelancable()));
		return sReturn;
	}

	@Override
	public Integer getJetMinumumPourBlesser() {
		Integer result = 0;
		Integer aResult = regles.stream().filter(r -&gt; r.getJetMinumumPourBlesser() &gt; 0)
				.mapToInt(r -&gt; r.getJetMinumumPourBlesser()).min().getAsInt();
		return (aResult != null) ? aResult : result;
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetBlessuresBlessuresMortelleEtOnArrete() {
		Map&lt;Integer, Integer&gt; mReturn = new HashMap&lt;&gt;();
		Integer value;
		for (IRegleAttaque regle : regles) {
			for (Entry&lt;Integer, Integer&gt; entry : regle.getMapJetBlessuresBlessuresMortelleEtOnArrete().entrySet()) {
				value = mReturn.computeIfAbsent(entry.getKey(), k -&gt; 0);
				value += entry.getValue();
				mReturn.put(entry.getKey(), value);
			}
		}
		return mReturn;
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetBlessuresBlessuresMortellesEtContinue() {
		Map&lt;Integer, Integer&gt; mReturn = new HashMap&lt;&gt;();
		Integer value;
		for (IRegleAttaque regle : regles) {
			for (Entry&lt;Integer, Integer&gt; entry : regle.getMapJetBlessuresBlessuresMortellesEtContinue().entrySet()) {
				value = mReturn.computeIfAbsent(entry.getKey(), k -&gt; 0);
				value += entry.getValue();
				mReturn.put(entry.getKey(), value);
			}
		}
		return mReturn;
	}

	@Override
	public Map&lt;Integer, Integer&gt; getMapJetBlessureBonusPA() {
		Map&lt;Integer, Integer&gt; mReturn = new HashMap&lt;&gt;();
		Integer value;
		for (IRegleAttaque regle : regles) {
			for (Entry&lt;Integer, Integer&gt; entry : regle.getMapJetBlessureBonusPA().entrySet()) {
				value = mReturn.computeIfAbsent(entry.getKey(), k -&gt; 0);
				value += entry.getValue();
				mReturn.put(entry.getKey(), value);
			}
		}
		return mReturn;
	}

	@Override
	public Integer getBonusNombreAttaque() {
		return regles.stream().mapToInt(r -&gt; r.getBonusNombreAttaque()).sum();
	}

	@Override
	public Integer getMalusSurAttrition() {
		return regles.stream().mapToInt(r -&gt; r.getMalusSurAttrition()).sum();
	}

	@Override
	public boolean isIgnoreSauvegardeInvulnerable() {
		return regles.stream().anyMatch(r -&gt; r.isIgnoreSauvegardeInvulnerable());
	}

	@Override
	public boolean isIgnoreSauvegardeCouvert() {
		return regles.stream().anyMatch(r -&gt; r.isIgnoreSauvegardeInvulnerable());
	}

	@Override
	public boolean isIgnoreIgnoreBlessure() {
		return regles.stream().anyMatch(r -&gt; r.isIgnoreIgnoreBlessure());
	}
}</pre></td></tr></table></pre>
</div><div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="40"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.calculateur.warhammer.data.enumeration.EBlessure;

/**
 * Décorateur de règle pour la défense
 * @author phili
 *
 */
public class RegleDefenseDecorateur implements IRegleDefense{

	private final List&lt;IRegleDefense&gt; regles;
	
	public RegleDefenseDecorateur(List&lt;IRegleDefense&gt; regles) {
		this.regles = regles;
	}
	
	@Override
	public Integer getMalusJetTouche() {
		return regles.stream().mapToInt(r -&gt; r.getMalusJetTouche()).sum();
	}

	@Override
	public Integer getJetMinimumPourEtreTouche() {
		Integer iReturn = 0;
		Integer result = regles.stream().filter(r -&gt; r.getJetMinimumPourEtreTouche() &gt; 0).mapToInt(r -&gt; r.getJetMinimumPourEtreTouche()).max().getAsInt();
		return (result != null)?result:iReturn;
	}

	@Override
	public Integer getMalusForce() {
		return regles.stream().mapToInt(r -&gt; r.getMalusForce()).sum();
	}

	@Override
	public Integer getBonusEndurance() {
		return regles.stream().mapToInt(r -&gt; r.getBonusEndurance()).sum();
	}

	@Override
	public Integer getJetMinimumPourEtreBlesse() {
		Integer iReturn = 0;
		Integer result = regles.stream().filter(r -&gt; r.getJetMinimumPourEtreBlesse() &gt; 0).mapToInt(r -&gt; r.getJetMinimumPourEtreBlesse()).max().getAsInt();
		return (result != null)?result:iReturn;
	}

	@Override
	public Integer getMalusJetBlessure() {
		return regles.stream().mapToInt(r -&gt; r.getMalusJetBlessure()).sum();
	}

	@Override
	public Integer getNombrePointsViesMaximumPerdable() {
		Integer iReturn = 0;
		Integer result = regles.stream().filter(r -&gt; r.getNombrePointsViesMaximumPerdable() &gt; 0).mapToInt(r -&gt; r.getNombrePointsViesMaximumPerdable()).min().getAsInt();
		return (result != null)?result:iReturn;
	}

	@Override
	public Integer getMalusNombreAttaque() {
		return regles.stream().mapToInt(r -&gt; r.getMalusNombreAttaque()).sum();
	}

	@Override
	public boolean isSauvegardeCouvert() {
		return regles.stream().anyMatch(r -&gt; r.isSauvegardeCouvert());
	}

	@Override
	public boolean isIgnoreTestCommandement() {
		return regles.stream().anyMatch(r -&gt; r.isIgnoreTestCommandement());
	}

	@Override
	public Integer getBonusCommandement() {
		return regles.stream().mapToInt(r -&gt; r.getBonusCommandement()).sum();
	}

	@Override
	public boolean isIgnoreTestAttrition() {
		return regles.stream().anyMatch(r -&gt; r.isIgnoreTestAttrition());
	}

	@Override
	public boolean isIgnoreMalusAttrition() {
		return regles.stream().anyMatch(r -&gt; r.isIgnoreMalusAttrition());
	}

	@Override
	public Integer getJetSeReleve() {
		Integer iReturn = 0;
		Integer result = regles.stream().filter(r -&gt; r.getJetSeReleve() &gt; 0).mapToInt(r -&gt; r.getJetSeReleve()).min().getAsInt();
		return (result != null)?result:iReturn;
	}

	@Override
	public Set&lt;Integer&gt; getRelancesSeReleve() {
		Set&lt;Integer&gt; sReturn = new HashSet&lt;&gt;();
		regles.stream().forEach(r -&gt; sReturn.addAll(r.getRelancesSeReleve()));
		return sReturn;
	}

	@Override
	public Integer getJetSoreIgnoreBlessure(EBlessure typeBlessures) {
		Integer iReturn = 0;
		Integer result = regles.stream().filter(r -&gt; r.getJetSoreIgnoreBlessure(typeBlessures) &gt; 0).mapToInt(r -&gt; r.getJetSoreIgnoreBlessure(typeBlessures)).min().getAsInt();
		return (result != null)?result:iReturn;
	}

}</pre></td></tr></table></pre>
</div><b>Et les bâtiment</b><br />
Dans ce que j'ai rendu pour SMB116, je différentiais dans le calcul le bâtiment. Et c'était juste infernal. <br />
Du coup, dans la reprise, un bâtiment, c'est juste un identifiable qui a ses propres règles.<br />
Il suffit donc de faire une implémentation pour le bâtiment:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles;

import com.calculateur.warhammer.data.enumeration.EMouvement;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IBatiment;

/**
 * Règle pour l'attaquant étant dans un bâtiment
 * @author phili
 *
 */
public class RegleAttaqueBatiment extends RegleAttaqueBase&lt;IBatiment&gt; {

	private final IBatiment batiment;

	private final ESimule simule;

	private final EMouvement mouvementPrecedent;

	private final boolean isTirContreChargeAvant;

	private static final Integer BONUS_TOUCHE = -1;
	private static final Integer NO_BONUS = 1;

	public RegleAttaqueBatiment(IBatiment batiment, ESimule simule, EMouvement mouvementPrecedent,
			boolean isTirContreChargeAvant) {
		super(batiment);
		this.batiment = batiment;
		this.simule = simule;
		this.mouvementPrecedent = mouvementPrecedent;
		this.isTirContreChargeAvant = isTirContreChargeAvant;
	}

	@Override
	public Integer getBonusJetTouche() {
		Integer iReturn;
		if (mouvementPrecedent == EMouvement.IMMOBILE) {
			boolean isDefendable = batiment.isDefendable()
					&amp;&amp; (simule == ESimule.TIR_CONTRE_CHARGE || (!isTirContreChargeAvant &amp;&amp; simule == ESimule.CORPS_A_CORPS_APRES_ACTIVATION));
			boolean isCouvertLourd = batiment.isCouvertLourd() &amp;&amp; simule == ESimule.CORPS_A_CORPS_APRES_ACTIVATION &amp;&amp; mouvementPrecedent == EMouvement.IMMOBILE;
			iReturn = (isDefendable || isCouvertLourd)?BONUS_TOUCHE:NO_BONUS;
		} else {
			iReturn = NO_BONUS;
		}
		return iReturn;
	}
}</pre></td></tr></table></pre>
</div><div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles;

import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IBatiment;

public class RegleDefenseBatiment extends RegleDefenseBase&lt;IBatiment&gt;{

	private final IBatiment batiment;

	private final ESimule simule;
	
	private static final Integer BONUS_TOUCHE = -1;
	private static final Integer BONUS_COMMANDEMENT = 1;
	private static final Integer NO_BONUS = 0;

	public RegleDefenseBatiment(IBatiment batiment, ESimule simule) {
		super(batiment);
		this.batiment = batiment;
		this.simule = simule;
	}

	@Override
	public Integer getMalusJetTouche() {
		return (simule == ESimule.TIR_PHASE_TIR &amp;&amp; batiment.isCouvertDense()) ? BONUS_TOUCHE
				: NO_BONUS;
	}

	@Override
	public boolean isSauvegardeCouvert() {
		return simule == ESimule.TIR_PHASE_TIR ? batiment.isCouvertLeger() : false;
	}

	@Override
	public Integer getBonusCommandement() {
		return batiment.isExaltant() ? BONUS_COMMANDEMENT : NO_BONUS;
	}
}</pre></td></tr></table></pre>
</div><b>Design Pattern Stratégie</b><br />
Au final, on a aussi utilisé le Design Pattern <a href="https://en.wikipedia.org/wiki/Strategy_pattern" target="_blank">Stratégie</a>.<br />
<br />
L'utilisateur ne voit que la règle, mais on l'obtient de différente façon, en changeant de stratégie en somme.<br />
<br />
<b>Et en cas de nouvelles règles (ou d'oubli...)</b><br />
Effectivement, la dernière fois, j'avais oublié que si ils chargent ou si ils sont chargés, les marines on une attaque de plus.<br />
<br />
Et pour les démons, les rumeurs parlent d'un nouveau type de sauvegarde.<br />
<br />
Pas de problème, on l'ajoute dans l'interface et on l'implémente dans les deux classes (le décorateur et la base).<br />
<br />
<b>Bon, maintenant, il faut obtenir la règle: le Design Pattern Factory</b> <br />
 <br />
Pour obtenir la règle, on va utiliser le Design Pattern <a href="https://en.wikipedia.org/wiki/Factory_method_pattern" target="_blank">Factory</a>.<br />
<br />
Ici, on a pas besoin, à ce stade, d'implémenter. On définit un contrat.<br />
<br />
Définir ce contrat sera important pour pouvoir faire l'injection de dépendance quand on ajoutera Spring.<br />
<br />
Soit:<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles.factory;

import com.calculateur.warhammer.base.exception.FunctionnalExeption;
import com.calculateur.warhammer.data.regles.IRegleAttaque;
import com.calculateur.warhammer.data.regles.IRegleDefense;

/**
 * Factory qui va créer la règle
 * @author phili
 *
 */
public interface FactoryRegle {

	/**
	 * 
	 * @param parametres Les paramère nécessaires propre à la faction
	 * @return Les régles d'attaques de la faction
	 * @throws FactoryReglesException Si une exception s'est produite lors de la fabrication des rècles
	 * @throws FunctionnalExeption Si après vérification, il manque une règle
	 */
	IRegleAttaque getRegleAttaque(String parametres)throws FactoryReglesException,FunctionnalExeption;
	
	/**
	 * 
	 * @param parametres Les paramère nécessaires propre à la faction
	 * @return Les régles de défense la faction
	 * @throws FactoryReglesException Si une exception s'est produite lors de la fabrication des rècles
	 * @throws FunctionnalExeption Si après vérification, il manque une règle
	 */
	IRegleDefense getRegleDefense(String parametres)throws FactoryReglesException,FunctionnalExeption;
}</pre></td></tr></table></pre>
</div>Même si je ne vais pas parler des exceptions Java (pour le moment), on note que la Factory jette une exception utilisateur (FunctionnalExeption) et une si elle n'arrive pas à fabriquer la règle (FactoryReglesException).<br />
<br />
On passe sous forme de String (comme &quot;isFigurinePerdu : true&quot; par exemple) pour pouvoir paramètrer la règle.<br />
<br />
On a aussi, comme c'est d'ailleurs l'usage en Java une Factoy pour la Factory.<br />
<div class="bbcode_container">
	<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>
	<td style="border: 0; padding: 0; text-align: left">Code  :</td>
	<td style="border: 0; padding: 0; text-align: right"><a href="#" onclick="return ano_selectionnerCode(this);">Sélectionner tout</a> -
	<a href="#" onclick="return ano_etendreCode(this);">Visualiser dans une fenêtre à part</a></td></tr></table>
	<pre class="bbcode_code" style="height:204px;"><table cellspacing="0" cellpadding="0"><tr><td valign="top" width="33"><div style="border: 1px dashed gray; padding-left: 5px; padding-right: 5px; margin-right: 5px; text-align: right; font-family: monospace">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td valign="top"><pre style="margin: 0">
package com.calculateur.warhammer.data.regles.factory;

/**
 * Factory pour fabriquer la Factory de règle.
 * @author phili
 *
 */
public interface FactoryFactoryRegle {

	/**
	 * 
	 * @param aClass Class de factory
	 * @return La factory de règles
	 * @throws FactoryReglesException
	 */
	FactoryRegle getFactory(String aClass)throws FactoryReglesException;
}</pre></td></tr></table></pre>
</div>On utilisera sans doute l'introspection mais ça, c'est une autre histoire.</blockquote>

]]></content:encoded>
			<dc:creator>PhilippeGibault</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/280180-philippegibault/b10354/gerer-regles-warhammer-40k-design-pattern/</guid>
		</item>
		<item>
			<title>Et le rapport du projet</title>
			<link>https://www.developpez.net/forums/blogs/280180-philippegibault/b10353/rapport-projet/</link>
			<pubDate>Sun, 10 Jul 2022 09:58:02 GMT</pubDate>
			<description>Qui sera le rapport pour le...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">Qui sera le rapport pour le brouillon:<br />
<br />
<a href="https://drive.google.com/file/d/1f0D7c8l1UBLc90MkhSAvs4AnxogeMA2B/view?usp=sharing" target="_blank">Lien</a></blockquote>

]]></content:encoded>
			<dc:creator>PhilippeGibault</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/280180-philippegibault/b10353/rapport-projet/</guid>
		</item>
		<item>
			<title>Comment vendre le projet au professeur de SMB116</title>
			<link>https://www.developpez.net/forums/blogs/280180-philippegibault/b10352/vendre-projet-professeur-smb116/</link>
			<pubDate>Sun, 10 Jul 2022 09:33:05 GMT</pubDate>
			<description>Déjà, il faut vendre le...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">Déjà, il faut vendre le projet (ce qui est d'ailleurs noté).<br />
<br />
Voici ce que j'ai écrit pour le vendre:<br />
<br />
<b>Introduction</b><br />
Le projet a été réfléchit, et n'ayant pas eu le temps de finir le brouillon, ni de réfléchir à un autre projet, je vais donc m'arrêter sur le calculateur de morts à Warhammer 40K.<br />
Prenons une situation typique de ce jeux, à savoir 10 Spaces Marines tirant au bolter sur 10 guerriers nécrons.<br />
<br />
Le space marine doir d'abord toucher. Il a une CT de 3+. De fait, il a une probabilité de réussir de 2/3.<br />
Ensuite, il doit blesser. Son Bolter a une force de 4, contre une endurance de 4, ce qui fait que le guerrier nécron est blessé sur 4+, soit une probabilité de 1/2.<br />
Mais le nécron a une armure de 4+. Le bolter du Space Marine a un modificateur de -1. La sauvegarde du guerrier nécron a donc 1/3 de chance d'être sauvegardé (et 2/3 de mourir).<br />
Mais le guerrier nécron peut se relever sur 5+, relançant les 1.<br />
<br />
<br />
Au final, on a des probabilités conditionnelles, ou de manière plus simple un arbre de probabilité.<br />
Deux chemins ont intéressants:<br />
<br />
    Le cas où le guerier Nécron ne se relève pas, et n'a pas pu retenter sa chance (2/3 * 1/2 * 2/3 *1/2 = 4/36 = 1/9)<br />
    Le cas où le guerier Nécron ne se relève pas, mais a retenté sa chance (2/3 * 1/2 * 1/6 * 2/3 = 4/108 = 1/27)<br />
<br />
<br />
Au final, pour un Sapce Marine, la probabilité de tuer un guerrier nécron est de 1/9 + 1/27 = (3 + 1)/27 = 4/27.<br />
Au final, 10 Spaces Marines tuent (4 * 20)/27 = 2.96... nécrons (un bolter tirant 2 fois soit 20 tir).<br />
Donc, au pire, 10 Spaces Marines tuent 2 guerriers nécrons. Au mieux, il en tuent 3.<br />
L'objectif de mon application est de faire ce genre de calcul, à la phase de tir, de corps à corps et si j'ai le courage à la phase de morale.<br />
J'ai un concurrent<br />
Ce type d'application existe (Math 40K). Néanmoins, j'ai l'intention de ne surtout pas prendre la même approche.<br />
L'application Math 40K demande de saisir différent paramètres.<br />
J'ai l'intention de faire sélectionner, via Menu de sélection/Case à cocher les paramètres du calcul<br />
Car effectivement, les jets dépends de beaucoup de paramètres, comme le chapitre (les Dark Angel ajoute +1 au jet de touche si ils n'ont pas bouger), de personnages aux alentours<br />
(le général permet de relancer les 1 pour toucher, le lieutenant permet de relancer les 1 pour blesser...), des décors (+1 sauvegarde pour les couverts...).<br />
Je vais donc présenter ma version, mais selon le temps..., c'est succeptible d'évoluer.<br />
<b>Projet Base</b><br />
Un JAR de base pour contractualisé les DAO, les services, des exceptions.. Utilisation de Maven.<br />
Projet calcul<br />
C'est l'arbre de probabilité en question. Projet Maven (réuilisable), et utilisation de Apache Math pour avoir les probabilités sous formes fractionnaires. TU avec JUnit et Mockito pour Mocker les interfaces.<br />
<b>Projet DTO</b><br />
Je vais partir des DTO. C'est un travail de modélisation conséquent ici, notament celui de modéliser l'ensemble des unités de Warhammer 40K. Utilisation de Maven.<br />
<b>Projet XML</b><br />
XML étant vu comme une entité, la couche DAO. Il faut bien remplir une BDD, et remplir des XML me paraît plus simple. Utilisation de JAXB et Maven.<br />
<b>Projet SQLLite</b><br />
L'idée est de fabriquer le fichier pour l'application Androïd. Utilisation de JPA, Spring et Maven. Idée de passer du XML -&gt; DTO -&gt; Entité JPA pour SQLLite et gérer un fichier de BDD.<br />
<b>Ou un projet serveur</b><br />
BDD PostgreSQL (+ Docker) contenant l'information, mise à disposition par un serveur (Spring, Spring Boot, JPA°. Utilisation de Maven.<br />
<b>Et le projet Androïd</b><br />
Dépend du projet de base, de calcul et de DTO. BDD locale (en l'occurence SQL Lite) directement rempli par fichier ou mise à jour serveur.<br />
Utilisatn de Dagger pour l'injection de dépendance.</blockquote>

]]></content:encoded>
			<dc:creator>PhilippeGibault</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/280180-philippegibault/b10352/vendre-projet-professeur-smb116/</guid>
		</item>
		<item>
			<title><![CDATA[C'est décidé, je fais un blog sur la reprise de mon projet de calculateur à 40K]]></title>
			<link>https://www.developpez.net/forums/blogs/280180-philippegibault/b10351/c-decide-fais-blog-reprise-projet-calculateur-40k/</link>
			<pubDate>Sun, 10 Jul 2022 09:27:20 GMT</pubDate>
			<description>Cette année a été dure. 
...</description>
			<content:encoded><![CDATA[<blockquote class="blogcontent restore">Cette année a été dure.<br />
<br />
J'ai eu des problème de santé, de travail aussi (du à mes problème de santé).<br />
<br />
Et pour couronner le tout, j'ai eu l'idée de suivre au CNAM le module <a href="https://formation.cnam.fr/rechercher-par-discipline/conception-et-developpement-pour-systemes-mobiles-508833.kjsp" target="_blank">SMB116</a>.<br />
Ce qui a été chronophage pour moi.<br />
<br />
Mais je l'ai eu, de justesse sans doute (je n'ai eu qu'un 12 à l'examen) mais je l'ai eu quand même.<br />
<br />
Je n'ai pas vraiment eu le temps de réviser, notamment suite aux nombreux problèmes évoqués, mais j'ai travaillé les TP,  et surtout le projet à rendre à la fin.<br />
<br />
Cette idée de projet m'est venu parce que je joue à Warhammer40K (<a href="https://warhammer40000.com/wp-content/uploads/2020/07/FRE-40K9_Basic_Rules.pdf" target="_blank">ici</a> et <a href="https://www.goonhammer.com/warhammer-40k-9th-edition/" target="_blank">ici</a> pour ceux qui ne connaissent pas )<br />
<br />
Il faut dire que j'ai su vendre le projet, et j'ai eu 5/5 (après l'avoir beaucoup travailler).<br />
<br />
Seulement, je dois être honnête avec moi même, je ne suis pas satisfait du résultat. Je n'ai pas réussi à coller la BDD (pour faciliter l'utilisation), et j'ai des choses que je referai différemment). <br />
<br />
Néanmoins, l'expérience était intéressante et instructif.<br />
<br />
Déjà, j'ai pu voir le gap entre &quot;le besoin du métier&quot; (ici, c'est à dire moi même) et le produit final, ainsi que le principe de réalité qui m'a fait revoir à la baisse mes ambitions.<br />
<br />
Néanmoins, j'ai décidé de reprendre le projet de zéro, pas à pas.<br />
<br />
Ce que j'ai donc rendu pour le module SMB116 n'est donc que le prototype.<br />
<br />
Et je me suis dit que même si je ne suis pas un vrai informaticien (mais expérimenté quand même), donner des étapes du projets pourraient être intéressants.<br />
<br />
D'où ce blog.</blockquote>

]]></content:encoded>
			<dc:creator>PhilippeGibault</dc:creator>
			<guid isPermaLink="true">https://www.developpez.net/forums/blogs/280180-philippegibault/b10351/c-decide-fais-blog-reprise-projet-calculateur-40k/</guid>
		</item>
	</channel>
</rss>
