À mes yeux, la programmation défensive se fonde sur la méfiance quant à l'environnement sur lequel on s'exécute (les données lues sont-elles valides, le service distant que nous appelons est-il fiable, etc.) et repose surtout sur :
- Des objets métiers contenant en leur sein des contrôles "d'invariants" : à n'importe quel moment, un objet métier que j'ai doit se considérer valide du point de vue du contenu de ses variables membres.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| /**
* Un code de commune.
*/
public class CodeCommune extends Id
{
/** Ensemble des codes communes valides connus. Il a été préalablement alimenté en cache statique. */
private static Set<CodeCommune> COMMUNES = new HashSet<>();
/**
* Vérification de la validité de l'objet métier.
*/
public void anomalies(Anomalies anomalies)
{
// Le code de la commune doit être alimenté.
if (isBlank(getId()))
anomalies.declare(ERREUR, "anomalie.code_commune_vide");
else
{
// Le code de la commune doit être associé à un code département.
if (getCodeDepartement() == null || isBlank(getCodeDepartement().getId()))
anomalies.declare(ERREUR, "anomalie.code_commune_sans_departement", this);
else
{
// Le département doit être valide.
anomalies.examine(getCodeDepartement());
// Vérifier que la commune est valide.
if (COMMUNES.contains(this) == false)
anomalies.declare(ERREUR, "anomalie.code_commune_non_francais", this);
}
}
}
// ...Autres méthodes de l'objet non présentées ici...
} |
L'idée étant d'arriver à une situation où "L'on peut transporter dans son programme un objet aussi faux que l'on veut, mais l'on ne peut pas l'utiliser." :
Je viens de lire un objet de la base, ce n'est pas moi qui l'ai écrit, et il est complètement baroque. Je peux faire autant d'appels de setters que je veux pour le corriger.
Mais (selon la rigueur que l'on s'impose) :
Moyenne : aucun service ou DAO ne peut utiliser l'objet tant qu'il n'aura pas une liste d'anomalies d'auto-contrôle vide,
Forte : aucun de ses getters ne renverra de valeur tant qu'il ne sera pas valide, et ces méthodes lèveront une exception à la place.
- Emploi d'assertions : pré-conditions, post-conditions.
- Emploi d'un jeu d'exceptions élaborées pour décrire tous les problèmes rencontrés avec gestion de leur propagation, enrichissement et rethrow, catch aux endroits appropriés si légitimité à le faire.
- Typage fort. Aucun String, int, double, ne se balade tout nu dans des paramètres de fonctions, si possible, quand il serait possible d'utiliser un objet métier à la place.
par exemple, la fonction présentée est bonne : payTo(Account $to, $amount)
alors que celle-ci payTo($banque, $agence, $guichet, $numeroCompte, $amount) serait plus risquée : banque + agence + guichet + numéro de compte = Account, certes, mais il n'y a plus de vérification aisée que ces quatre premiers paramètres sont bien cohérents entre-eux.
+ d'autres pratiques, nombreuses, comme : des DAO vérifiant avant écriture / update que les objets qu'ils vont persister sont valides avant de le faire.
Partager