Seven Wonders 5 – Springons tout ça

Nous allons springifier tout ça : le PartieManager va devenir un service qui va se faire injecter pour le test.

On ajoute spring dans le pom du sevenwonders-business :

<properties>
		<java-version>1.6</java-version>
		<org.springframework-version>3.0.6.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.9</org.aspectj-version>
		<org.slf4j-version>1.5.10</org.slf4j-version>
	</properties>

	<dependencies>

		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

	</dependencies>

Et on passe partieManagerImpl en service :

@Service("partieManager")
public class PartieManagerImpl implements PartieManager {
//...
}

Et dans notre test où nous avions

@Before
  public void setUp() {
    partieManager = new PartieManagerImpl();
//...
}

Nous allons laisser faire ça à Spring :

Créons un conf-context.xml dans src-main-resources avec :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
	default-autowire="byType">

	<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />

	<bean id="partieManager" class="fr.mathieujjava.sevenwonders.PartieManagerImpl" />
</beans>

et modifions notre classe de test :

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/conf-context.xml"})
public class PartieManagerImplTest {

  @Autowired
  private PartieManagerImpl partieManager;
  //...
}

Lançons le test : ça couine sur le log4j.

log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUnit4ClassRunner).
log4j:WARN Please initialize the log4j system properly.

Pas de problème créons un log4j.properties au même endroit que le root-context et mettons-y une config de base :

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x – %m%n

Nous pouvons créer nos premiers beans de merveilles dans le merveilles-beans.xml qu’on met dans src/main/resources (je ne suis pas très certain que ça soit le bon endroit, si qqn a une meilleure suggestion je suis preneur). Voilà la première merveille :

<bean id="merveille1" class="fr.mathieujjava.sevenwonders.Merveille">
		<constructor-arg name="nomEn" value="The Colossus of Rhodes" />
		<constructor-arg name="nomFr" value="Le Colosse de Rhodes" />
		<constructor-arg name="face" value="1" />
		<constructor-arg name="texte" value="1er étage : 3 points de victoire; 2d étage : Fournit 2 puissance militaire; 3ème étage : 7 points de victoire" />
		<constructor-arg name="ressource" value="Minerai" />
		<constructor-arg name="cout" value="B,BB,BBB" />
	</bean>

Nous avons un

Error creating bean with name ‘merveille1’ defined in class path resource [merveilles-beans.xml]: Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)

Cela vient de la transformation du cout : il nous faut un transformateur de String en Cout :

public class CoutEditor extends PropertyEditorSupport {
  public void setAsText(String text) throws IllegalArgumentException {
    Cout cout = new Cout(0);

    for (char c : text.toCharArray()) {
      switch (c) {
      case 'B':
        cout.ajoute(Ressource.Bois);
        break;
      case 'P':
        cout.ajoute(Ressource.Pierre);
        break;
      case 'R':
        cout.ajoute(Ressource.Brique);
        break;
      case 'M':
        cout.ajoute(Ressource.Minerai);
        break;
      case 'T':
        cout.ajoute(Ressource.Tissu);
        break;
      case 'V':
        cout.ajoute(Ressource.Verre);
        break;
      case 'Y':
        cout.ajoute(Ressource.Papyrus);
        break;
      }
    }
    setValue(cout);
  }
}

et on déclare ce constructeur de Cout dans le xml :

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
		<property name="customEditors">
			<map>
				<entry key="fr.mathieujjava.sevenwonders.Cout" value="fr.mathieujjava.sevenwonders.propertyeditor.CoutEditor" />
			</map>
		</property>
	</bean>

Une fois ceci fait, nous pouvons effectivement créer en beans les 7 merveilles et de la même façon les cartes dans cartes-context.xml.
J’ai collé le coutEditor dans conf-context général vu qu’il sert pour les merveilles et les cartes.

Avec les cartes et les merveilles, nous pouvons développer les fonctions qui utilisent les vraies cartes, ainsi quand un joueur joue une carte de Commerce il s’attend à recevoir des sous :

public static void effectueCarte(Partie partie, Joueur joueur, Carte carte) {
    if ("Tavern".equals(carte.getNomEn())) {
      joueur.modifieNombrePieces(5);
    } else if ("Vineyard".equals(carte.getNomEn())) {
      joueur.modifieNombrePieces(partie.compteNombreCartes(joueur, TypeCarte.MatierePremiere, true, true, true));
    } else if ("Bazar".equals(carte.getNomEn())) {
      joueur.modifieNombrePieces(partie.compteNombreCartes(joueur, TypeCarte.Manufacture, true, true, true));
    } else if ("Chamber of Commerce".equals(carte.getNomEn())) {
      joueur.modifieNombrePieces(2 * partie.compteNombreCartes(joueur, TypeCarte.Manufacture, false, true, false));
    } else if ("Lighthouse".equals(carte.getNomEn())) {
      joueur.modifieNombrePieces(partie.compteNombreCartes(joueur, TypeCarte.Commerce, false, true, false));
    } else if ("Haven".equals(carte.getNomEn())) {
      joueur.modifieNombrePieces(partie.compteNombreCartes(joueur, TypeCarte.MatierePremiere, false, true, false));
    }
  }

Là dessus j’ai quelques réserves : utiliser le nom anglais comme identifiant de la carte ne me semble pas très propre, mais il existe des cartes avec le même nom (donc fonctionnellement la même carte) à différents âges, donc utiliser un vrai identifiant générerait des identifiants identiques pour la carte Bazar de l’age 1 et de l’age 2…

De même, on va pouvoir coder les autres actions que la défausse, dont la construction de la merveille :

case Merveille:
      joueur.getMain().remove(action.getCarte());
      if (joueur.getEtageMerveille() == 3) throw new Exception("Le joueur a déjà tout construit");
      switch (joueur.getEtageMerveille()) {
      case 0 :
      }
      paieCout(partie, joueur, joueur.merveille....);
      break;

On le voit rapidement, faire un switch sur l’étage pour savoir quel coût payer dans la merveille n’est pas très propre, il faudrait transformer les 4 coûts en tableau, ce qui permet en plus de savoir combien il y a d’étages.

Refactorons Merveille ainsi :

public class Merveille {
  Cout[] coutsAge;
    //...

  public Merveille(String nomEn, String nomFr, Integer face, String texte, Ressource ressource, Cout... cout) {
    //...
    coutsAge = cout;
  }

  public Cout getCoutAge(Integer age) {
    return coutsAge[age-1];
  }
}

Après une modif rapide des codes unitaires pour utiliser le bon getter plutôt que les 4 anciens getters (un par age), tout fonctionne bien.

Maintenant que le coeur de l’appli fonctionne, faisons une petite webapp pour montrer tout ça au reste de l’humanité.

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

w

Connexion à %s