GLO-4002 - Site du cours 2023

Temps estimé: 20 minutes

Exercices préparatoires IV

Question 1

Donnez un exemple où le non respect du SRP serait acceptable et plus pratique.

Exemple 1: Une méthode createOrUpdate. Il peut être plus pratique dans certains cas de créer une méthode createOrUpdate plutôt que de créer deux méthodes séparées et ajouter la logique qui pointe vers l'une de ces méthodes si la plupart des clients ont ce besoin.

Exemple 2: La méthode pop d'une Queue qui retourne à la fois l'élément, à la fois le supprime de la Queue. Le séparer en 2 méthodes front et pop forcerait les clients à toujours faire front avant de pop ce qui 1) serait désagréable 2) pourrait causer des problèmes de concurrence.

Question 2

Voici plusieurs classes qui ont toutes besoin de collaborer avec un API externe pour aller chercher des données:

public class ExternalApiAccountRepository implements AccountRepository {

  private static final int RETRY_LIMIT = 4;

  // ...

  public Account findById(AccountNumber accountNo) {
    int retryCount = 1;

    while (retryCount < RETRY_LIMIT) {
      HttpResponse response = httpClient.get(String.format("awesome.api/accounts/%s" + accountNo);

      if (response.getStatus() == 200) {
        return accountMapper.fromBody(response.getBody());
      }

      if (response.getStatus() == 404) {
        throw AccountNotFoundException.withNumber(accountNo);
      }

      if (response.getStatus() >= 400 && response.getStatus() < 500) {
        throw new InvalidArgumentException();
      }

      retryCount++;

      try {
        Thread.sleep(1000 * (retryCount * retryCount));
      } catch (InterruptedException ignored) {
      }
    }
  }
}

public class ExternalApiTransactionRepository implements TransactionRepository {

  private static final int RETRY_LIMIT = 4;

  // ...

  public Account save(Transaction transaction) {
    int retryCount = 1;

    while (retryCount < RETRY_LIMIT) {
      HttpResponse response = httpClient.post("awesome.api/transactions", transactionMapper.toRequest(transaction));

      if (response.getStatus() == 201) {
        return transactionMapper.fromBody(response.getBody());
      }

      if (response.getStatus() == 400) {
        throw new InvalidTransactionException();
      }

      if (response.getStatus() >= 400 && response.getStatus() < 500) {
        throw new InvalidArgumentException();
      }

      retryCount++;

      try {
        Thread.sleep(1000 * (retryCount * retryCount));
      } catch (InterruptedException ignored) {
      }
    }
  }
}

public class ExternalApiTaxService implements TaxService {

  private static final int RETRY_LIMIT = 4;

  // ...
}

Toutefois, on remarque que chacune de ces classes possède la même logique pour faire les requêtes, gérer les erreurs et appliquer une stratégie de retry. Il y a donc une certaine duplication de code.

Selon les deux options suivantes pour corriger ce problème de duplication :

Option 1

Créer une classe ApiClient qui gère les erreurs et toute la logique commune. Les classes qui collaborent avec l'API externe héritent de cette classe.

Option 2

Créer une classe ApiClient qui gère les erreurs et toute la logique commune. Les classes qui collaborent avec l'API externe se font injecter cette classe.

En quoi privilégier l'héritage (option 1) plutôt que la composition (option 2) ferait en sorte que le SRP serait violé?

Le fait qu'une classe hérite des fonctionnalités de gestion d'erreurs et toute la logique qui s'y rattache ne respecte pas le SRP, car c'est encore ladite classe qui possède cette responsabilité plutôt que de la déléguer à une autre classe.

De plus, cela crée un fort couplage vers la classe héritée qui pourrait provoquer un effet d'avalanche (ripple effect) hors de contrôle en cas de changement.

Question 3

Qu'est-ce qui est incorrect dans ce schéma du modèle hexagonal:

Exemple de Modèle hexagonal

Le DIP n'est pas respecté puisque des modules de haut niveau dépendent de modules de bas niveau.

Une classe de la couche applicative dépend directement d'une classe de l'infrastructure.

Une classe du domaine dépend directement d'une classe de l'infrastructure.