Skip to content

8. [TD]: Рівень [metier]

Ключові слова: багатошарова архітектура, Spring, ін’єкція залежностей.

8.1. Support

 

Папка [support / chap-08] містить проект Eclipse до цього розділу.

8.2. Налаштування Maven

  

Проєкт [elections-metier-dao-jdbc] базуватиметься на проєкті [elections-dao-jdbc-01]. Ми встановимо файл jar цього проєкту в локальне сховище Maven:

 

Після цього конфігурація Maven для проекту Eclipse [elections-metier-dao-jdbc] виглядає наступним чином:

  

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

    <!-- плагіни -->
    <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>
            <!-- для встановлення артефакту проєкту в локальне сховище Maven -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
            </plugin>
        </plugins>
    </build>
</project>
  • рядки 21–25: залежність від JAR-файлу проекту [elections-jdbc-01]. Ці рядки беремо безпосередньо з файлу [pom.xml] того проекту, який ми хочемо імпортувати:

<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>
...
  • рядки 26–37: залежності, необхідні для тестування. Хоча вони містяться у файлі [pom.xml] проекту [elections-jdbc-01], ми змушені вказати їх заново, оскільки через їхній атрибут [<scope>test</scope>] вони не були включені до JAR-файлу, розміщеного в локальному репозиторії Maven;

8.3. Конфігурація 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 {
}
  • рядок 7: імпортуємо всі біни, визначені в шарі [DAO];
  • рядок 8: активується кеш. Бін [CacheManager] не перевизначається, оскільки він уже визначений у шарі [DAO];
  • рядок 9: шар [métier] визначає нові біни в пакеті [elections.metier.service];

8.4. Інтерфейс [IElectionsMetier]

  

Рівень [metier] матиме інтерфейс, наведений у розділі 4.2. Нагадаємо його:


public interface IElectionsMetier {

    public ListeElectorale[] getListesElectorales();

    public int getNbSiegesAPourvoir();

    public double getSeuilElectoral();

    public void recordResultats(ListeElectorale[] listesElectorales);

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

}

8.5. Клас реалізації [ElectionsMetier]

  

Цей клас реалізує інтерфейс [IElectionsMetier]. Запропонована реалізація матиме таку структуру:


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 {

    // точка доступу до шару [dao], інстанційованого [Spring]
    @SuppressWarnings("unused")
    @Autowired
    private IElectionsDao electionsDao;

    // розрахунок отриманих місць
  @Override
    public ListeElectorale[] calculerSieges(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[calculerSieges] not yet implemented");
    }

    // списки, що беруть участь у виборах
  @Override
    public ListeElectorale[] getListesElectorales() {
        throw new RuntimeException("[getListesElectorales] not yet implemented");
    }

    // збереження результатів
  @Override
    public void recordResultats(ListeElectorale[] listesElectorales) {
        throw new RuntimeException("[recordResultats] not yet implemented");
    }

    // кількість місць, що підлягають розподілу
    @Override
    public int getNbSiegesAPourvoir() {
        throw new RuntimeException("[getNbSiegesAPourvoir] not yet implemented");
    }

    // виборчий поріг
    @Override
    public double getSeuilElectoral() {
        throw new RuntimeException("[getSeuilElectoral] not yet implemented");
    }

}
  • рядок 10: клас [ElectionsMetier] є компонентом Spring;
  • рядок 11: клас [ElectionsMetier] реалізує інтерфейс [IElectionsMetier];
  • рядки 15–16: ін’єкція Spring посилання на рівень [DAO];
  • рядки 37 і 44: кількість місць, що підлягають заповненню, та виборчий поріг приховані;

Завдання: напишіть клас [ElectionsMetier]. Скористайтеся коментарями, якщо вони є. Не намагайтеся обробляти (try / catch) винятки типу [ElectionsException], які надходять із шару [DAO]. Ми дозволимо їм просуватися до рівня [UI]. Оскільки клас [ElectionsException] є типом неконтрольованого винятку, немає обов’язку обробляти його за допомогою (try / catch).


8.6. Тестовий клас

  

Тестовий клас JUnit матиме такий вигляд:


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 {

    // шар [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() {
        // створюється таблиця з 7 списків кандидатів (назва, голоси)

        // розраховуємо кількість місць для кожного списку

        // перевіряються результати (мандати, голоси, виключаються)

        // тимчасовий
        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() {
        // заносимо в базу даних таблицю з 7 списками кандидатів

        // фіксуються голоси

        // розраховуються місця, отримані кожним зі списків

        // перевіряються результати (мандати, голоси, виключаються)

        // тимчасовий
        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)
    // написати тест, який викликає виняток типу [ElectionsException]
    public void calculSieges3() {
        // створюється масив із 25 списків кандидатів, кожен із яких має 1 голос
        // усі 25 списків матимуть однакову кількість голосів (4%)

        // розподіл місць — зазвичай має вийти ElectionsException
        // з виборчим бар’єром у 5%

        // тимчасовий
        throw new RuntimeException("[calculSieges3] not yet implemented");
    }

    /**
     * enregistrement des résultats de l'élection
     */
    @Test
    public void ecritureResultatsElections() {
        // створюється таблиця з 7 списків кандидатів

        // фіксуємо кількість голосів

        // розраховуються місця, отримані кожним зі списків

        // результати записуються в базу даних

        // переглядаються списки в базі даних

        // перевіряються результати (мандати, голоси, виключаються)

        // тимчасовий
        throw new RuntimeException("[ecritureResultatsElections] not yet implemented");
    }
}

Завдання: напишіть чотири методи тестування, користуючись коментарями. Слід пам’ятати, що під час виконання цих методів поле [electionsMetier] у рядку 19 вже було ініціалізовано. Перевірте, чи проходить тест JUnit.


8.7. Створення архіву шару [metier]

Як і у випадку з шаром [DAO], ми розміщуємо архів проєкту [elections-metier-dao-jdbc] у локальному репозиторії Maven:

 

Примітка: ця операція може завершитися невдачею, якщо ваш проект Eclipse пов’язаний із JRE (Java Runtime Environment) замість JDK (Java Development Kit). Щоб це з’ясувати, виконайте дії, описані в розділі 3.1. Якщо ви виявите, що у вас встановлено JRE, а не JDK, пов’яжіть свій проєкт із JDK, як зазначено в цьому розділі.

8.8. Conclusion

Нагадаємо загальну архітектуру додатка [Elections], який ми зараз створюємо:

Ми створили шари [metier] та [dao]. Тепер ми створимо шар [ui]. Ми запропонуємо дві реалізації для цього шару:

  • реалізацію «консольної»
  • реалізація з графічним інтерфейсом