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