J'ai grandi en utilisant un principe de conception et de consommation d'interfaces qui dit essentiellement: «ne demandez que ce dont vous avez besoin»
Par exemple, si j'ai un tas de types qui peuvent être supprimés, je ferai une Deletable
interface:
interface Deletable {
void delete();
}
Ensuite, je peux écrire une classe générique:
class Deleter<T extends Deletable> {
void delete(T t) {
t.delete();
}
}
Ailleurs dans le code, je demanderai toujours la plus petite responsabilité possible pour répondre aux besoins du code client. Donc, si je dois seulement supprimer un File
, je demanderai toujours un Deletable
, pas un File
.
Ce principe est-il de notoriété publique et a-t-il déjà un nom accepté? Est-ce controversé? Est-ce discuté dans les manuels?
object-oriented
interfaces
solid
single-responsibility
glenviewjeff
la source
la source
Réponses:
Je crois que cela fait référence à ce que Robert Martin appelle le principe de ségrégation d'interface . Les interfaces sont séparées en petites et concises afin que les consommateurs (clients) n'aient qu'à connaître les méthodes qui les intéressent. Vous pouvez en savoir plus sur SOLID .
la source
Pour développer la très bonne réponse de Vadim, je répondrai à la question "est-ce controversé" par "non, pas vraiment".
En général, la ségrégation des interfaces est une bonne chose, car elle réduit le nombre global de «raisons de changer» des différents objets impliqués. Le principe de base est que lorsqu'une interface avec plusieurs méthodes doit être modifiée, par exemple pour ajouter un paramètre à l'une des méthodes d'interface, tous les consommateurs de l'interface doivent au moins être recompilés, même s'ils n'ont pas utilisé la méthode qui a changé.. "Mais c'est juste une recompilation!", Je vous entends dire; cela peut être vrai, mais gardez à l'esprit qu'en général, tout ce que vous recompilez doit être expulsé dans le cadre d'un correctif logiciel, quelle que soit l'importance de la modification du binaire. Ces règles ont été conçues à l'origine au début des années 90, lorsque la station de travail de bureau moyenne était moins puissante que le téléphone dans votre poche, la connexion à distance de 14,4 bauds était flamboyante et 3,5 "1,44 Mo de" disquettes "étaient le principal support amovible. Même à l'ère actuelle de la 3G / 4G, les utilisateurs d'Internet sans fil ont souvent des plans de données avec des limites, donc lors de la publication d'une mise à niveau, moins il y a de binaires à télécharger, mieux c'est.
Cependant, comme toutes les bonnes idées, la ségrégation d'interface peut mal tourner si elle n'est pas correctement mise en œuvre. Tout d'abord, il est possible qu'en séparant les interfaces tout en gardant l'objet qui implémente ces interfaces (remplissant les dépendances) relativement inchangé, vous pouvez vous retrouver avec une "Hydra", un parent de l'anti-modèle "God Object" où le la nature omnisciente et toute-puissante de l'objet est cachée aux dépendants par les interfaces étroites. Vous vous retrouvez avec un monstre à plusieurs têtes qui est au moins aussi difficile à entretenir que l'Objet divin le serait, plus les frais généraux liés à la maintenance de toutes ses interfaces. Il n'y a pas un nombre fixe d'interfaces que vous ne devriez pas dépasser, mais chaque interface que vous implémentez sur un seul objet doit être préfacée en répondant à la question "Cette interface contribue-t-elle à l'objet"
Deuxièmement, une interface par méthode peut ne pas être nécessaire, malgré ce que SRP peut vous dire. Vous pouvez vous retrouver avec "code ravioli"; tant de morceaux de la taille d'une bouchée qu'il est difficile de retracer pour savoir exactement où les choses se produisent réellement. Il n'est pas non plus nécessaire de diviser une interface avec deux méthodes si tous les utilisateurs actuels de cette interface ont besoin des deux méthodes. Même si l'une des classes dépendantes n'a besoin que d'une des deux méthodes, il est généralement acceptable de ne pas diviser l'interface si ses méthodes ont conceptuellement une cohésion très élevée (de bons exemples sont les "méthodes antonymiques" qui sont exactement opposées les unes aux autres).
La ségrégation d'interface doit être basée sur les classes qui dépendent de l'interface:
S'il n'y a qu'une seule classe dépendante de l'interface, ne séparez pas. Si la classe n'utilise pas une ou plusieurs des méthodes d'interface, et qu'elle est le seul consommateur de l'interface, il y a de fortes chances que vous n'ayez pas dû exposer ces méthodes en premier lieu.
S'il y a plus d'une classe qui dépend de l'interface et que toutes les personnes à charge utilisent toutes les méthodes de l'interface, ne séparez pas; si vous devez changer l'interface (pour ajouter une méthode ou modifier une signature), tous les consommateurs actuels seront affectés par la modification, que vous les sépariez ou non (bien que si vous ajoutez une méthode dont au moins une personne à charge n'aura pas besoin, envisagez soigneusement si le changement doit être implémenté en tant que nouvelle interface, héritant éventuellement de celle existante).
S'il existe plusieurs classes dépendantes de l'interface et qu'elles n'utilisent pas toutes les mêmes méthodes, c'est un candidat à la ségrégation. Regardez la "cohérence" de l'interface; toutes les méthodes poursuivent-elles un seul objectif de programmation très spécifique? Si vous pouvez identifier plus d'un objectif principal pour l'interface (et ses implémenteurs), envisagez de fractionner les interfaces le long de ces lignes pour créer des interfaces plus petites avec moins de «raisons de changer».
la source
IBaz : IFoo, IBar
et de l'exiger à la place.IFoo
et à mesureIBar
, la définition d'un compositeIFooBar
peut être une bonne idée, mais si les interfaces sont finement divisées, il est facile de finir par nécessiter des dizaines de types d'interface distincts. Considérez les fonctionnalités suivantes que les collections peuvent avoir: énumérer, rapporter le nombre, lire le nième élément, écrire le nième élément, insérer avant le nième élément, supprimer le nième élément, nouvel élément (agrandir la collection et retourner l'index du nouvel espace) et ajouter. Neuf méthodes: ECRWIDNA. Je pourrais probablement décrire des dizaines de types qui prendraient naturellement en charge de nombreuses combinaisons différentes.