Voici un problème que je rencontre fréquemment: qu'il y ait un projet de boutique en ligne ayant une classe de produit. Je souhaite ajouter une fonctionnalité qui permet aux utilisateurs de publier des avis sur un produit. J'ai donc une classe Review qui fait référence à un produit. Maintenant, j'ai besoin d'une méthode qui répertorie toutes les critiques d'un produit. Il y a deux possibilités:
(UNE)
public class Product {
...
public Collection<Review> getReviews() {...}
}
(B)
public class Review {
...
static public Collection<Review> forProduct( Product product ) {...}
}
En regardant le code, je choisirais (A): Ce n'est pas statique et il n'a pas besoin de paramètre. Cependant, je sens que (A) viole le principe de responsabilité unique (SRP) et le principe ouvert-fermé (OCP) tandis que (B) ne:
(SRP) Lorsque je veux changer la façon dont les avis sont collectés pour un produit, je dois changer la classe de produit. Mais il ne devrait y avoir qu'une seule raison pour changer la classe de produit. Et ce n'est certainement pas les critiques. Si j'emballe toutes les fonctionnalités qui ont quelque chose à voir avec les produits dans Product, elles seront bientôt claquées.
(OCP) Je dois changer la classe de produit pour l'étendre avec cette fonctionnalité. Je pense que cela viole la partie «Fermé pour le changement» du principe. Avant de recevoir la demande du client pour la mise en œuvre des avis, j'ai considéré le produit comme terminé et je l'ai "fermé".
Quoi de plus important: suivre les principes SOLID, ou avoir une interface plus simple?
Ou est-ce que je fais quelque chose de mal ici?
Résultat
Wow, merci pour toutes vos bonnes réponses! Il est difficile d'en choisir une comme réponse officielle.
Permettez-moi de résumer les principaux arguments des réponses:
- pro (A): OCP n'est pas une loi et la lisibilité du code est également importante.
- pro (A): la relation d'entité doit être navigable. Les deux classes peuvent connaître cette relation.
- pro (A) + (B): faites les deux et déléguez (A) à (B) afin que le produit soit moins susceptible d'être modifié à nouveau.
- pro (C): mettez les méthodes de recherche dans la troisième classe (service) où elle n'est pas statique.
- contra (B): empêche la moquerie dans les tests.
Quelques choses supplémentaires que mes collèges au travail ont apportées:
- pro (B): notre framework ORM peut générer automatiquement le code pour (B).
- pro (A): pour des raisons techniques de notre framework ORM, il faudra dans certains cas modifier l'entité "fermée", indépendamment de l'endroit où se trouve le chercheur. Je ne pourrai donc pas toujours m'en tenir à SOLID de toute façon.
- contra (C): trop de bruit ;-)
Conclusion
J'utilise à la fois (A) + (B) avec délégation pour mon projet actuel. Dans un environnement axé sur les services, cependant, j'irai avec (C).
Assert(5 = Math.Abs(-5));
Abs()
n'est pas le problème, tester quelque chose qui en dépend. Vous n'avez pas de couture pour isoler le code sous test (CUT) dépendant pour utiliser une maquette. Cela signifie que vous ne pouvez pas le tester en tant qu'unité atomique et que tous vos tests deviennent des tests d'intégration qui testent la logique de l'unité. Un échec dans un test peut être en CUT ou en Abs()
(ou son code dépendant) et supprime les avantages de diagnostic des tests unitaires.