Skip to content

8. [TD]: Die Schicht [metier]

Stichworte: mehrschichtige Architektur, Spring, Abhängigkeitsinjektion.

8.1. Support

 

Der Ordner „[support / chap-08]“ enthält das Eclipse-Projekt zu diesem Kapitel.

8.2. Maven-Konfiguration

  

Das Projekt [elections-metier-dao-jdbc] baut auf dem Projekt [elections-dao-jdbc-01] auf. Wir werden die JAR-Datei dieses Projekts im lokalen Maven-Repository installieren:

 

Danach sieht die Maven-Konfiguration des Eclipse-Projekts [elections-metier-dao-jdbc] wie folgt aus:

  

<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>

    <!-- Plugins -->
    <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>
            <!-- zur Installation des Projekt-Artefakts im lokalen Maven-Repository -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </build>
</project>
  • Zeilen 21–25: die Abhängigkeit von der JAR-Datei des Projekts [elections-jdbc-01]. Diese Zeilen übernehmen wir direkt aus der Datei [pom.xml] des Projekts, das wir importieren möchten:

<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>
...
  • Zeilen 26–37: Die für die Tests erforderlichen Abhängigkeiten. Obwohl sie in der Datei [pom.xml] des Projekts [elections-jdbc-01] vorhanden sind, müssen wir sie erneut einfügen, da sie aufgrund ihres Attributs [<scope>test</scope>] nicht in die JAR-Datei aufgenommen wurden, die im lokalen Maven-Repository abgelegt ist;

8.3. Spring-Konfiguration

  

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 {
}
  • Zeile 7: Wir importieren alle in der Schicht [DAO] definierten Beans;
  • Zeile 8: Der Cache wird aktiviert. Der Bean [CacheManager] wird nicht neu definiert, da er bereits in der Schicht [DAO] definiert ist;
  • Zeile 9: Die Schicht [métier] definiert neue Beans im Paket [elections.metier.service];

8.4. Die Schnittstelle [IElectionsMetier]

  

Die Schicht [metier] verfügt über die in Abschnitt 4.2 vorgestellte Schnittstelle. Wir wiederholen sie hier:


public interface IElectionsMetier {

    public ListeElectorale[] getListesElectorales();

    public int getNbSiegesAPourvoir();

    public double getSeuilElectoral();

    public void recordResultats(ListeElectorale[] listesElectorales);

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

}

8.5. Die Implementierungsklasse [ElectionsMetier]

  

Diese Klasse implementiert die Schnittstelle [IElectionsMetier]. Die vorgeschlagene Implementierung wird folgende Struktur aufweisen:


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 {

    // der Zugriffspunkt auf die von [Spring] instanziierte Schicht [dao]
    @SuppressWarnings("unused")
    @Autowired
    private IElectionsDao electionsDao;

    // Berechnung der gewonnenen Sitze
  @Override
    public ListeElectorale[] calculerSieges(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[calculerSieges] not yet implemented");
    }

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

    // Speicherung der Ergebnisse
  @Override
    public void recordResultats(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[recordResultats] not yet implemented");
    }

    // Anzahl der zu vergebenden Sitze
    @Override
    public int getNbSiegesAPourvoir() {
        throw new RuntimeException("[getNbSiegesAPourvoir] not yet implemented");
    }

    // Wahlhürde
    @Override
    public double getSeuilElectoral() {
        throw new RuntimeException("[getSeuilElectoral] not yet implemented");
    }

}
  • Zeile 10: Die Klasse [ElectionsMetier] ist eine Spring-Komponente;
  • Zeile 11: Die Klasse [ElectionsMetier] implementiert die Schnittstelle [IElectionsMetier];
  • Zeilen 15–16: Spring-Injektion einer Referenz auf die Schicht [DAO];
  • Zeilen 37 und 44: Die Anzahl der zu besetzenden Sitze und die Wahlhürde werden ausgeblendet;

Aufgabe: Schreiben Sie die Klasse [ElectionsMetier]. Nutzen Sie dabei die Kommentare, sofern vorhanden. Versuchen Sie nicht, Ausnahmen vom Typ [ElectionsException], die aus der Schicht [DAO] zurückgemeldet werden, abzufangen (try / catch). Wir lassen sie bis zur Schicht [UI] weiterleiten. Da es sich bei der Klasse [ElectionsException] um einen unkontrollierten Ausnahmetyp handelt, besteht keine Verpflichtung, sie mit einem (try / catch) zu behandeln.


8.6. Die Testklasse

  

Die Testklasse JUnit hat folgende Form:


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 {

    // Ebene [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() {
        // Es wird eine Tabelle mit den 7 Kandidatenlisten (Name, Stimmen) erstellt

        // Berechnung der Sitze für jede Liste

        // Die Ergebnisse werden überprüft (Sitze, Stimmen, Ausschluss)

        // vorläufig
        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() {
        // Die Tabelle mit den 7 Kandidatenlisten wird in die Datenbank übernommen

        // Die Stimmen werden festgeschrieben

        // Berechnung der von jeder Liste erzielten Sitze

        // Die Ergebnisse werden überprüft (Sitze, Stimmen, Ausschluss)

        // vorläufig
        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)
    // einen Test schreiben, der eine Ausnahme vom Typ [ElectionsException] auslöst
    public void calculSieges3() {
        // Es wird ein Array mit 25 Kandidatenlisten erstellt, die jeweils 1 Stimme erhalten
        // Die 25 Listen erhalten jeweils die gleiche Stimmenzahl (4 %)

        // Sitzberechnung – normalerweise sollte man ein Ergebnis wie ElectionsException erhalten
        // bei einer Wahlhürde von 5 %

        // vorläufig
        throw new RuntimeException("[calculSieges3] not yet implemented");
    }

    /**
     * enregistrement des résultats de l'élection
     */
    @Test
    public void ecritureResultatsElections() {
        // Es wird eine Tabelle mit den 7 Kandidatenlisten erstellt

        // die Stimmen werden fest vorgegeben

        // die von jeder Liste erzielten Sitze werden berechnet

        // Die Ergebnisse werden in der Datenbank gespeichert

        // Die Listen in der Datenbank werden erneut ausgelesen

        // Die Ergebnisse werden überprüft (Sitze, Stimmen, Ausschluss)

        // vorläufig
        throw new RuntimeException("[ecritureResultatsElections] not yet implemented");
    }
}

Aufgabe: Schreiben Sie die vier Testmethoden mithilfe der Kommentare. Beachten Sie, dass bei der Ausführung dieser Methoden das Feld [electionsMetier] in Zeile 19 bereits initialisiert wurde. Überprüfen Sie, ob der Test JUnit erfolgreich ist.


8.7. Erstellung des Archivs der Schicht [metier]

Wie bereits bei der Schicht [DAO] legen wir das Archiv des Projekts [elections-metier-dao-jdbc] im lokalen Maven-Repository ab:

 

Hinweis: Dieser Vorgang kann fehlschlagen, wenn Ihr Eclipse-Projekt mit einem JRE (Java Runtime Environment) statt mit einem JDK (Java Development Kit) verknüpft ist. Um dies festzustellen, gehen Sie wie in Abschnitt 3.1 beschrieben vor. Wenn Sie feststellen, dass Sie ein JRE und kein JDK haben, verknüpfen Sie Ihr Projekt wie in diesem Abschnitt beschrieben mit einem JDK.

8.8. Conclusion

Zur Erinnerung: Hier die allgemeine Architektur der Anwendung [Elections], die wir gerade erstellen:

Wir haben die Schichten [metier] und [dao] erstellt. Nun werden wir die Schicht [ui] erstellen. Für diese Schicht werden wir zwei Implementierungen vorschlagen:

  • eine „Konsolen“-Implementierung
  • eine Implementierung mit grafischer Benutzeroberfläche