Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Projet Calculateur
#22
Et enfin fini la validation du calcul.

Cette fois, on passe à la Factory (le Design Pattern du GOFF).

Pour commencer, comme on fait de l'introspection, on utilise cette librairie (dans le POM service):
Code:
<!--Introspection-->
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>${jar.reflexion}</version>
            <exclusions>
                <exclusion>
                     <groupId>org.slf4j</groupId>
                     <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
Le métier (data et calcul) n'aimant pas avoir des librairies (sauf Apache Common Math pour le calcul), l'injection se fait chez service où on a le célèbre framework Spring.

Pour commencer, on définit la factory par un contrat simple:
Code:
package com.calculateur.warhammer.calcul.mort.factory.validation;

import java.util.List;

import com.calculateur.warhammer.calcul.mort.factory.exception.SimulationException;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;

/**
* Factory pour les validations avant le calcul
* @author phili
*
*/
public interface IFactoryValidation {

    /**
     *
     * @param simule Ce qui est simulé.
     * @return La liste des validations à effectuer avant la simulation/calcul.
     * @throws SimulationException Si ce qui est demandé est incohérent.
     */
    List<IValidationCalcul<IArmeDeTir>> getValidationsActionTir(ESimule simule) throws SimulationException;
    
    /**
     *
     * @param simule Ce qui est simulé.
     * @return La liste des validations pour un corps à corps.
     * @throws SimulationException Si ce qui est demandé est incohérent.
     */
    List<IValidationCalcul<IArmeDeCorpsACorps>> getValidationsActionCorpsACorps(ESimule simule) throws SimulationException;
}

Et son implémentation:
Code:
package com.calculateur.warhammer.calcul.mort.factory.validation;

import java.util.List;

import com.calculateur.warhammer.calcul.mort.factory.exception.SimulationException;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;

public class FactoryValidation implements IFactoryValidation{

    private final List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir;
    
    private final List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge;
    
    private final List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps;

    public FactoryValidation(List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir,
            List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge,
            List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps) {
        this.validationsPhaseTir = validationsPhaseTir;
        this.validationsTirContreCharge = validationsTirContreCharge;
        this.validationsCorpsACorps = validationsCorpsACorps;
    }

    @Override
    public List<IValidationCalcul<IArmeDeTir>> getValidationsActionTir(ESimule simule) throws SimulationException {
        if(simule.isActionCorpACorp()) {
            Object[] param = {simule};
            throw new SimulationException(param, "simule.tir.demande.melee");
        }
        return (simule == ESimule.TIR_PHASE_TIR)?validationsPhaseTir:validationsTirContreCharge;
    }

    @Override
    public List<IValidationCalcul<IArmeDeCorpsACorps>> getValidationsActionCorpsACorps(ESimule simule)
            throws SimulationException {
        if(simule.isActionTir()) {
            Object[] param = {simule};
            throw new SimulationException(param, "simule.melee.demande.tir");
        }
        return validationsCorpsACorps;
    }
}

Si l'utilisateur envoie de la merde, on lui chie dessus:
Code:
package com.calculateur.warhammer.calcul.mort.factory.exception;

import com.calculateur.warhammer.base.exception.ParameterizedFunctionnalException;

public class SimulationException extends ParameterizedFunctionnalException{

    /**
     *
     */
    private static final long serialVersionUID = 1L;
    
    private static final String RES = "com.calculateur.warhammer.calcul.mort.simulation.simulation";
    
    public SimulationException(Object[] objets, String key) {
        super(RES,objets, key);
    }

}

Le tout testé, contrairement à certains rigolos et leurs projets à 60 Millions d'€:
Code:
package com.calculateur.warhammer.calcul.mort.factory.validation;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import com.calculateur.warhammer.calcul.mort.factory.exception.SimulationException;
import com.calculateur.warhammer.calcul.mort.parametres.ParametresBuilder;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.calcul.mort.validation.ValidationArmeCorpsACorps;
import com.calculateur.warhammer.calcul.mort.validation.ValidationArmeTir;
import com.calculateur.warhammer.calcul.mort.validation.ValidationDefenseur;
import com.calculateur.warhammer.calcul.mort.validation.ValidationTirContreChargeMelee;
import com.calculateur.warhammer.calcul.mort.validation.ValidationTypeArme;
import com.calculateur.warhammer.data.enumeration.ESimule;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;

public class FactoryValidationTest {

    private static final Locale[] LOCALES = {Locale.FRANCE,Locale.ENGLISH};
    
    private final List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir;    
    private final List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge;    
    private final List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps;
    
    private final IFactoryValidation factory;
    
    public FactoryValidationTest() {
        validationsPhaseTir = Arrays.asList(new ValidationArmeTir(),new ValidationDefenseur<>());
        validationsTirContreCharge = Arrays.asList(new ValidationTypeArme<>(),new ValidationTirContreChargeMelee());
        validationsCorpsACorps = Arrays.asList(new ValidationArmeCorpsACorps(),new ValidationDefenseur<>());
        
        ParametresBuilder builder = ParametresBuilder.getInstance();
        validationsPhaseTir.forEach(builder::addValidationPhaseTir);
        validationsTirContreCharge.forEach(builder::addValidationTirContreCharge);
        validationsCorpsACorps.forEach(builder::addValidationCorpsACorps);
        
        factory = builder.build().getFactoryValidation();
    }
    
    @Test
    public void testContextTir() {
        List<IValidationCalcul<IArmeDeTir>> lTest;
        List<IValidationCalcul<IArmeDeTir>> lAttendue;
        for(ESimule simule:ESimule.values()) {
            try {
                lTest = factory.getValidationsActionTir(simule);
                Assertions.assertThat(simule.isActionTir()).isTrue();
                lAttendue = (simule == ESimule.TIR_PHASE_TIR)?validationsPhaseTir:validationsTirContreCharge;
                Assertions.assertThat(lTest).isEqualTo(lAttendue);
            }catch(SimulationException ex) {
                Assertions.assertThat(simule.isActionCorpACorp()).isTrue();
                valideException(ex);
            }
        }
    }
    
    @Test
    public void testContextMelee() {
        List<IValidationCalcul<IArmeDeCorpsACorps>> lTest;
        for(ESimule simule:ESimule.values()) {
            try {
                lTest = factory.getValidationsActionCorpsACorps(simule);
                Assertions.assertThat(lTest).isEqualTo(validationsCorpsACorps);
            }catch(SimulationException ex) {
                Assertions.assertThat(simule.isActionTir()).isTrue();
                valideException(ex);
            }
        }
    }
    
    private void valideException(SimulationException ex) {
        String key = ex.getMessage();
        Object[] params = ex.getObjets();
        Assertions.assertThat(key).isNotNull();
        ResourceBundle res;
        for(Locale locale:LOCALES) {
            res = ex.getResourceBundle(locale);
            ex.traduireLibelles(locale);
            Assertions.assertThat(ex.getMessage()).isEqualTo(MessageFormat.format(res.getString(key), params));
        }
    }
}


Selon le principe TDD, le test a été écrit avant l'implémentation.

Reste à injecter ça dans les services de Spring (indirectement testé, vu que j'ai aussi des tests Back-end).

Pour cela, on définit le paramétrage:
Code:
package com.calculateur.warhammer.calcul.mort.parametres;

import com.calculateur.warhammer.calcul.mort.factory.validation.IFactoryValidation;

/**
* Paramètres pour fabriquer les factory nécessaires au calcul.
* @author phili
*
*/
public interface IParametres {

    IFactoryValidation getFactoryValidation();
}

Et l'implémentation concrète:
Code:
package com.calculateur.warhammer.calcul.mort.parametres;

import java.util.List;

import com.calculateur.warhammer.calcul.mort.factory.validation.FactoryValidation;
import com.calculateur.warhammer.calcul.mort.factory.validation.IFactoryValidation;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;

public class Parametres implements IParametres{

    private List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir;    
    private List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge;    
    private List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps;
    
    @Override
    public IFactoryValidation getFactoryValidation() {
        return new FactoryValidation(validationsPhaseTir, validationsTirContreCharge, validationsCorpsACorps);
    }

    //Getter Setter
    public List<IValidationCalcul<IArmeDeTir>> getValidationsPhaseTir() {
        return validationsPhaseTir;
    }

    public void setValidationsPhaseTir(List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir) {
        this.validationsPhaseTir = validationsPhaseTir;
    }

    public List<IValidationCalcul<IArmeDeTir>> getValidationsTirContreCharge() {
        return validationsTirContreCharge;
    }

    public void setValidationsTirContreCharge(List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge) {
        this.validationsTirContreCharge = validationsTirContreCharge;
    }

    public List<IValidationCalcul<IArmeDeCorpsACorps>> getValidationsCorpsACorps() {
        return validationsCorpsACorps;
    }

    public void setValidationsCorpsACorps(List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps) {
        this.validationsCorpsACorps = validationsCorpsACorps;
    }
}

Pour construire ce truc, on utilise un autre Design Pattern du GOFF: Builder.
Code:
package com.calculateur.warhammer.calcul.mort.parametres;

import java.util.ArrayList;
import java.util.List;

import com.calculateur.warhammer.base.builder.IBuilder;
import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;

public class ParametresBuilder implements IBuilder<IParametres>{

    private final List<IValidationCalcul<IArmeDeTir>> validationsPhaseTir;    
    private final List<IValidationCalcul<IArmeDeTir>> validationsTirContreCharge;    
    private final List<IValidationCalcul<IArmeDeCorpsACorps>> validationsCorpsACorps;
    
    private ParametresBuilder() {
        validationsPhaseTir = new ArrayList<>();
        validationsTirContreCharge = new ArrayList<>();
        validationsCorpsACorps = new ArrayList<>();
    }
    
    public static ParametresBuilder getInstance() {
        return new ParametresBuilder();
    }
    
    @Override
    public IParametres build() {
        Parametres pReturn = new Parametres();
        
        pReturn.setValidationsPhaseTir(validationsPhaseTir);
        pReturn.setValidationsTirContreCharge(validationsTirContreCharge);
        pReturn.setValidationsCorpsACorps(validationsCorpsACorps);
        
        return pReturn;
    }

    public ParametresBuilder addValidationPhaseTir(IValidationCalcul<IArmeDeTir> validation) {
        validationsPhaseTir.add(validation);
        return this;
    }
    
    public ParametresBuilder addValidationTirContreCharge(IValidationCalcul<IArmeDeTir> validation) {
        validationsTirContreCharge.add(validation);
        return this;
    }
    
    public ParametresBuilder addValidationCorpsACorps(IValidationCalcul<IArmeDeCorpsACorps> validation) {
        validationsCorpsACorps.add(validation);
        return this;
    }
}

Reste plus qu'à injecter ça avec les beans de Spring.
Pour savoir ce qu'il faut injecter, on fait une classe d'introspection:
Code:
package com.calculateur.warhammer.service.configuration.introspection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.reflections.Reflections;

import com.calculateur.warhammer.calcul.mort.validation.IValidationCalcul;
import com.calculateur.warhammer.calcul.mort.validation.annotations.ValidationCorpsACorps;
import com.calculateur.warhammer.calcul.mort.validation.annotations.ValidationPhaseTir;
import com.calculateur.warhammer.calcul.mort.validation.annotations.ValidationTirContreCharge;
import com.calculateur.warhammer.data.identifiable.IArmeDeCorpsACorps;
import com.calculateur.warhammer.data.identifiable.IArmeDeTir;

public class ValidationIntrospectionUtil {

    private static final String PACKAGE = "com.calculateur.warhammer.calcul.mort.validation";
    
    private ValidationIntrospectionUtil() {
    }
    
    @SuppressWarnings("unchecked")
    public static List<IValidationCalcul<IArmeDeTir>> getListValidationPhaseTir() throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        List<IValidationCalcul<IArmeDeTir>> lReturn = new ArrayList<>();
        Reflections reflections = new Reflections(PACKAGE);
        
        Set<Class<?>> liste =
                reflections.getTypesAnnotatedWith(ValidationPhaseTir.class);
        Constructor<?> constructor;
        IValidationCalcul<IArmeDeTir> validation;
        for(Class<?> aClass:liste) {
            constructor = aClass.getConstructors()[0];
            validation = (IValidationCalcul<IArmeDeTir>)constructor.newInstance();
            lReturn.add(validation);
        }
        return lReturn;
    }
    
    @SuppressWarnings("unchecked")
    public static List<IValidationCalcul<IArmeDeTir>> getListValisationTirContreCharge()throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        List<IValidationCalcul<IArmeDeTir>> lReturn = new ArrayList<>();
        Reflections reflections = new Reflections(PACKAGE);
        
        Set<Class<?>> liste =
                reflections.getTypesAnnotatedWith(ValidationTirContreCharge.class);
        Constructor<?> constructor;
        IValidationCalcul<IArmeDeTir> validation;
        for(Class<?> aClass:liste) {
            constructor = aClass.getConstructors()[0];
            validation = (IValidationCalcul<IArmeDeTir>)constructor.newInstance();
            lReturn.add(validation);
        }
        return lReturn;
    }
    
    @SuppressWarnings("unchecked")
    public static List<IValidationCalcul<IArmeDeCorpsACorps>> getListValisationCorpsACorps()throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        List<IValidationCalcul<IArmeDeCorpsACorps>> lReturn = new ArrayList<>();
        Reflections reflections = new Reflections(PACKAGE);
        
        Set<Class<?>> liste =
                reflections.getTypesAnnotatedWith(ValidationCorpsACorps.class);
        Constructor<?> constructor;
        IValidationCalcul<IArmeDeCorpsACorps> validation;
        for(Class<?> aClass:liste) {
            constructor = aClass.getConstructors()[0];
            validation = (IValidationCalcul<IArmeDeCorpsACorps>)constructor.newInstance();
            lReturn.add(validation);
        }
        return lReturn;
    }
}

On a utilisé les annotations précédentes. Comme ça, on peut toujours ajouter dynamiquement des validations, au cas où j'en ai oublié. Sait-on jamais.

La conf de Spring devient:
Code:
package com.calculateur.warhammer.service.configuration.general;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;

import com.calculateur.warhammer.base.traduction.ILangues;
import com.calculateur.warhammer.base.traduction.LanguesImplementation;
import com.calculateur.warhammer.calcul.mort.parametres.IParametres;
import com.calculateur.warhammer.calcul.mort.parametres.ParametresBuilder;
import com.calculateur.warhammer.calcul.pouvoir.psy.CalculProbaPouvoirPsy;
import com.calculateur.warhammer.calcul.pouvoir.psy.ICalculProbaPouvoirPsy;
import com.calculateur.warhammer.data.regles.factory.factory.FactoryFactoryRegles;
import com.calculateur.warhammer.data.regles.factory.factory.IFactoryFactoryRegle;
import com.calculateur.warhammer.service.configuration.introspection.ValidationIntrospectionUtil;

/**
* Configuration des services par Spring boot
* @author phili
*
*/
@ComponentScan(basePackages = {"com.calculateur.warhammer.service.service","com.calculateur.warhammer.service.mapping"})
public class AbstractConfigurationService {

    @Bean(name = "langues")
    public ILangues getLangues() {
        Locale defaut = Locale.FRANCE;
        Map<String, Locale> map = new HashMap<>();
        map.put("fr", Locale.FRANCE);
        map.put("en", Locale.ENGLISH);
        return new LanguesImplementation(defaut, map);
    }
    
    //Fabrication de règle
    @Bean(name = "factoryFactoryRegle")
    public IFactoryFactoryRegle getFactoryFactoryRegle() {
        return new FactoryFactoryRegles();
    }
    
    //Paramètres calcul
    @Bean(name = "parametres")
    public IParametres getParametres() throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        final ParametresBuilder builder = ParametresBuilder.getInstance();
        ValidationIntrospectionUtil.getListValidationPhaseTir().forEach(builder::addValidationPhaseTir);
        ValidationIntrospectionUtil.getListValisationTirContreCharge().forEach(builder::addValidationTirContreCharge);
        ValidationIntrospectionUtil.getListValisationCorpsACorps().forEach(builder::addValidationCorpsACorps);
        return builder.build();
    }
    
    //Calcul proba phase psychique
    @Bean("calculProbaPouvoirPsy")
    public ICalculProbaPouvoirPsy getCalculProbaPouvoirPsy() {
        return new CalculProbaPouvoirPsy();
    }
}

Si il y a une erreur lors de l'introspection, le serveur web ne démarre même pas. Comme les tests back-end fonctionnent...
Reply


Messages In This Thread
Projet Calculateur - by Philou - 12-09-2022, 09:47 AM
RE: Projet Calculateur - by Philou - 12-09-2022, 09:52 AM
RE: Projet Calculateur - by Philou - 17-09-2022, 09:14 PM
RE: Projet Calculateur - by Philou - 25-09-2022, 07:26 AM
RE: Projet Calculateur - by Philou - 25-09-2022, 08:54 AM
RE: Projet Calculateur - by Philou - 03-10-2022, 10:54 AM
RE: Projet Calculateur - by Philou - 16-10-2022, 11:36 AM
RE: Projet Calculateur - by Philou - 24-10-2022, 01:08 PM
RE: Projet Calculateur - by Philou - 25-10-2022, 09:52 AM
RE: Projet Calculateur - by sdm - 25-10-2022, 11:06 PM
RE: Projet Calculateur - by Philou - 25-10-2022, 11:23 PM
RE: Projet Calculateur - by Philou - 26-10-2022, 03:09 PM
RE: Projet Calculateur - by Philou - 25-10-2022, 11:25 PM
RE: Projet Calculateur - by Philou - 06-11-2022, 05:27 PM
RE: Projet Calculateur - by Philou - 19-11-2022, 10:36 AM
RE: Projet Calculateur - by Philou - 23-11-2022, 08:34 AM
RE: Projet Calculateur - by Philou - 24-11-2022, 08:49 PM
RE: Projet Calculateur - by Philou - 04-12-2022, 06:57 PM
RE: Projet Calculateur - by Philou - 11-12-2022, 12:11 PM
RE: Projet Calculateur - by Philou - 18-12-2022, 06:47 PM
RE: Projet Calculateur - by Philou - 19-12-2022, 09:05 PM
RE: Projet Calculateur - by Philou - 03-01-2023, 09:34 PM
RE: Projet Calculateur - by Philou - 07-01-2023, 06:36 PM
RE: Projet Calculateur - by Philou - 01-02-2023, 08:21 PM
RE: Projet Calculateur - by Philou - 16-02-2023, 08:31 PM
RE: Projet Calculateur - by Gaeriel - 17-02-2023, 10:56 PM
RE: Projet Calculateur - by Philou - 17-02-2023, 11:32 PM
RE: Projet Calculateur - by Philou - 05-03-2023, 05:45 PM
RE: Projet Calculateur - by Philou - 06-03-2023, 08:55 PM
RE: Projet Calculateur - by Philou - 08-03-2023, 08:28 PM
RE: Projet Calculateur - by Philou - 12-03-2023, 07:56 AM
RE: Projet Calculateur - by Philou - 23-03-2023, 06:44 PM
RE: Projet Calculateur - by Philou - 28-03-2023, 08:58 PM
RE: Projet Calculateur - by Philou - 27-04-2023, 08:05 PM
RE: Projet Calculateur - by Philou - 30-04-2023, 10:29 AM
RE: Projet Calculateur - by Philou - 02-05-2023, 08:03 AM
RE: Projet Calculateur - by Philou - 19-05-2023, 11:36 AM
RE: Projet Calculateur - by Philou - 19-05-2023, 05:44 PM
RE: Projet Calculateur - by Philou - 20-05-2023, 09:10 PM
RE: Projet Calculateur - by Philou - 22-06-2023, 07:38 AM
RE: Projet Calculateur - by Philou - 24-06-2023, 07:09 AM
RE: Projet Calculateur - by Philou - 26-06-2023, 07:44 PM
RE: Projet Calculateur - by Philou - 28-06-2023, 07:09 AM
RE: Projet Calculateur - by Philou - 28-06-2023, 07:43 PM
RE: Projet Calculateur - by Philou - 30-06-2023, 07:22 PM
RE: Projet Calculateur - by Philou - 08-08-2023, 07:54 PM
RE: Projet Calculateur - by Philou - 19-08-2023, 10:30 AM
RE: Projet Calculateur - by Philou - 19-08-2023, 06:20 PM
RE: Projet Calculateur - by Philou - 20-08-2023, 10:17 AM
RE: Projet Calculateur - by Philou - 22-01-2024, 09:02 PM
RE: Projet Calculateur - by Philou - 09-05-2024, 10:21 AM
RE: Projet Calculateur - by Philou - 11-05-2024, 05:41 PM
RE: Projet Calculateur - by Philou - 25-05-2024, 08:41 AM
RE: Projet Calculateur - by Philou - 24-04-2025, 04:09 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)