Skip to content

8. [TD]: Warstwa [metier]

Słowa kluczowe: architektura wielowarstwowa, Spring, wstrzykiwanie zależności.

8.1. Support

 

Folder [support / chap-08] zawiera projekt Eclipse z tego rozdziału.

8.2. Konfiguracja Maven

  

Projekt [elections-metier-dao-jdbc] będzie oparty na projekcie [elections-dao-jdbc-01]. Zainstalujemy plik JAR tego ostatniego projektu w lokalnym repozytorium Maven:

 

Po wykonaniu tej czynności konfiguracja Maven projektu Eclipse [elections-metier-dao-jdbc] wygląda następująco:

  

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-metier-dao-jdbc</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.7.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- DAO -->
        <dependency>
            <groupId>istia.st.elections</groupId>
            <artifactId>elections-dao-jdbc-01</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <!-- wtyczki -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>config.AppConfig</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
            <!-- do zainstalowania artefaktu projektu w lokalnym repozytorium Maven -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </build>
</project>
  • wiersze 21–25: zależność od pliku JAR projektu [elections-jdbc-01]. Wiersze te pobieramy bezpośrednio z pliku [pom.xml] projektu, który chcemy zaimportować:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>istia.st.elections</groupId>
    <artifactId>elections-dao-jdbc-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
...
  • wiersze 26–37: zależności niezbędne do przeprowadzenia testów. Mimo że znajdują się one w pliku [pom.xml] projektu [elections-jdbc-01], musimy je ponownie dodać, ponieważ ich atrybut [<scope>test</scope>] spowodował, że nie zostały one uwzględnione w pliku jar umieszczonym w lokalnym repozytorium Maven;

8.3. Konfiguracja Spring

  

package elections.metier.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@Import({ elections.dao.config.AppConfig.class })
@EnableCaching
@ComponentScan(basePackages = { "elections.metier.service" })
public class MetierConfig {
}
  • wiersz 7: importujemy wszystkie bean’y zdefiniowane w warstwie [DAO];
  • wiersz 8: włączamy pamięć podręczną. Nie redefiniujemy bean’a [CacheManager], ponieważ jest on już zdefiniowany w warstwie [DAO];
  • wiersz 9: warstwa [métier] definiuje nowe bean w pakiecie [elections.metier.service];

8.4. Interfejs [IElectionsMetier]

  

Warstwa [metier] będzie posiadać interfejs przedstawiony w paragrafie 4.2. Przypominamy go:


public interface IElectionsMetier {

    public ListeElectorale[] getListesElectorales();

    public int getNbSiegesAPourvoir();

    public double getSeuilElectoral();

    public void recordResultats(ListeElectorale[] listesElectorales);

    public ListeElectorale[] calculerSieges(ListeElectorale[] listesElectorales);

}

8.5. Klasa implementacyjna [ElectionsMetier]

  

Klasa ta implementuje interfejs [IElectionsMetier]. Proponowana implementacja będzie miała następującą strukturę:


package elections.metier.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import elections.dao.entities.ListeElectorale;
import elections.dao.service.IElectionsDao;

@Component
public class ElectionsMetier implements IElectionsMetier {

    // punkt dostępu do warstwy [dao] instancjonowanej przez [Spring]
    @SuppressWarnings("unused")
    @Autowired
    private IElectionsDao electionsDao;

    // obliczenie liczby uzyskanych mandatów
  @Override
    public ListeElectorale[] calculerSieges(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[calculerSieges] not yet implemented");
    }

    // listy startowe
  @Override
    public ListeElectorale[] getListesElectorales() {
        throw new RuntimeException("[getListesElectorales] not yet implemented");
    }

    // zapisanie wyników
  @Override
    public void recordResultats(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[recordResultats] not yet implemented");
    }

    // liczba mandatów do obsadzenia
    @Override
    public int getNbSiegesAPourvoir() {
        throw new RuntimeException("[getNbSiegesAPourvoir] not yet implemented");
    }

    // próg wyborczy
    @Override
    public double getSeuilElectoral() {
        throw new RuntimeException("[getSeuilElectoral] not yet implemented");
    }

}
  • wiersz 10: klasa [ElectionsMetier] jest komponentem Spring;
  • wiersz 11: klasa [ElectionsMetier] implementuje interfejs [IElectionsMetier];
  • wiersze 15–16: wstrzyknięcie przez Spring odniesienia do warstwy [DAO];
  • wiersze 37 i 44: liczba mandatów do obsadzenia oraz próg wyborczy są ukryte;

Zadanie do wykonania: napisz klasę [ElectionsMetier]. Skorzystaj z komentarzy, jeśli są dostępne. Nie próbuj przechwytywać (try / catch) wyjątków typu [ElectionsException], które są zgłaszane z warstwy [DAO]. Pozwolimy im przedostać się do warstwy [UI]. Ponieważ klasa [ElectionsException] jest typem wyjątku niekontrolowanego, nie ma obowiązku obsługi jej za pomocą bloku try/catch.


8.6. Klasa testowa

  

Klasa testowa JUnit będzie miała następującą postać:


package tests;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import dao.config.AppConfig;
import dao.entities.ElectionsException;
import metier.service.IElectionsMetier;

@SpringApplicationConfiguration(classes = MetierConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {

    // warstwa [métier]
    @Autowired
    static private IElectionsMetier electionsMetier;

    /**
     * vérification 1 : méthode de calcul des sièges on fixe en dur les listes
     */
    @Test
    public void calculSieges1() {
        // tworzy się tabelę 7 list wyborczych (nazwa, liczba głosów)

        // obliczamy liczbę mandatów dla każdej z list

        // sprawdza się wyniki (mandaty, głosy, eliminuje się)

        // tymczasowa
        throw new RuntimeException("[calculSieges1] not yet implemented");
    }

    /**
     * vérification 2 : méthode de calcul des sièges on demande les listes à la
     * couche [metier] puis on fixe en dur les voix
     */
    @Test
    public void calculSieges2() {
        // przenosimy do bazy tablicę zawierającą 7 list kandydatów

        // ustala się na stałe liczbę głosów

        // obliczamy liczbę mandatów uzyskanych przez każdą z list

        // sprawdza się wyniki (mandaty, głosy, eliminuje się)

        // tymczasowy
        throw new RuntimeException("[calculSieges2] not yet implemented");
    }

    /**
     * vérification 3 méthode de calcul des sièges on provoque une exception
     */
    @Test(expected = ElectionsException.class)
    // napisanie testu wywołującego wyjątek typu [ElectionsException]
    public void calculSieges3() {
        // tworzymy tablicę zawierającą 25 list kandydatów, z których każda ma 1 głos
        // wszystkie 25 list będzie miało taką samą liczbę głosów (4%)

        // obliczenie liczby mandatów – normalnie powinniśmy otrzymać wynik ElectionsException
        // przy progu wyborczym wynoszącym 5%

        // tymczasowe
        throw new RuntimeException("[calculSieges3] not yet implemented");
    }

    /**
     * enregistrement des résultats de l'élection
     */
    @Test
    public void ecritureResultatsElections() {
        // tworzymy tabelę 7 list wyborczych

        // ustala się na stałe liczbę głosów

        // oblicza się liczbę mandatów uzyskanych przez każdą z list

        // zapisywanie wyników w bazie danych

        // ponownie odczytuje się listy z bazy danych

        // sprawdza się wyniki (mandaty, głosy, eliminuje się)

        // tymczasowy
        throw new RuntimeException("[ecritureResultatsElections] not yet implemented");
    }
}

Zadanie: napisz cztery metody testowe, korzystając z komentarzy. Należy pamiętać, że w momencie wykonywania tych metod pole [electionsMetier] w wierszu 19 zostało już zainicjowane. Sprawdź, czy test JUnit przebiega pomyślnie.


8.7. Tworzenie archiwum warstwy [metier]

Podobnie jak w przypadku warstwy [DAO], umieszczamy archiwum projektu [elections-metier-dao-jdbc] w lokalnym repozytorium Maven:

 

Uwaga: operacja ta może zakończyć się niepowodzeniem, jeśli projekt Eclipse jest powiązany z JRE (Java Runtime Environment) zamiast z JDK (Java Development Kit). Aby to sprawdzić, postępuj zgodnie z instrukcjami zawartymi w punkcie 3.1. Jeśli okaże się, że masz plik JRE, a nie JDK, powiąż swój projekt z plikiem JDK zgodnie z instrukcjami zawartymi w tym punkcie.

8.8. Conclusion

Przypomnijmy ogólną architekturę aplikacji [Elections], którą właśnie tworzymy:

Stworzyliśmy warstwy [metier] i [dao]. Teraz zajmiemy się tworzeniem warstwy [ui]. Zaproponujemy dwie implementacje tej warstwy:

  • implementację „konsolową”
  • implementację z interfejsem graficznym