Regardons cela pratiquement

Obj 3sait maintenant Obj 4existe. Et alors? Pourquoi on s'en soucie?
DIP dit
"Les modules de haut niveau ne devraient pas dépendre de modules de bas niveau. Les deux devraient dépendre d'abstractions."
D'accord, mais tous les objets ne sont-ils pas des abstractions?
DIP dit aussi
"Les abstractions ne doivent pas dépendre des détails. Les détails doivent dépendre des abstractions."
OK mais, si mon objet est correctement encapsulé, cela ne cache-t-il aucun détail?
Certaines personnes insistent aveuglément sur le fait que chaque objet a besoin d'une interface de mot-clé. Je n'en fais pas partie. J'aime insister aveuglément sur le fait que si vous ne les utilisez pas maintenant, vous avez besoin d'un plan pour gérer plus tard quelque chose comme eux.
Si votre code est entièrement refactorable sur chaque version, vous pouvez simplement extraire les interfaces plus tard si vous en avez besoin. Si vous avez publié du code que vous ne voulez pas recompiler et que vous souhaitez que vous parliez via une interface, vous aurez besoin d'un plan.
Obj 3sait Obj 4existe. Mais Obj 3sait si le Obj 4béton est?
C'est ici pourquoi il est si agréable de NE PAS se propager newpartout. Si Obj 3je ne sais pas s'il Obj 4est concret, probablement parce qu'il ne l'a pas créé, alors si vous vous glissez plus tard et que vous vous transformez Obj 4en classe abstraite, Obj 3cela ne vous importera pas.
Si vous pouvez le faire, cela Obj 4a toujours été entièrement abstrait. La seule chose qui crée une interface entre eux dès le départ, c'est l'assurance que quelqu'un n'ajoutera pas accidentellement du code qui donne du Obj 4concret en ce moment. Les constructeurs protégés peuvent atténuer ce risque mais cela conduit à une autre question:
Obj 3 et obj 4 sont-ils dans le même package?
Les objets sont souvent regroupés d'une manière ou d'une autre (package, espace de noms, etc.). Lorsqu'ils sont regroupés judicieusement, les impacts plus probables au sein d'un groupe plutôt qu'entre les groupes changent.
J'aime grouper par fonctionnalité. Si Obj 3et Obj 4sont dans le même groupe et la même couche, il est très peu probable que vous en ayez publié un et que vous ne souhaitiez pas le refactoriser tout en n'ayant besoin de changer que l'autre. Cela signifie que ces objets sont moins susceptibles de bénéficier d'une abstraction entre eux avant d'avoir un besoin clair.
Si vous traversez une limite de groupe, c'est vraiment une bonne idée de laisser les objets de chaque côté varier indépendamment.
Cela devrait être aussi simple, mais malheureusement, Java et C # ont fait des choix malheureux qui compliquent cela.
En C #, il est de tradition de nommer chaque interface de mot-clé avec un Ipréfixe. Cela oblige les clients à SAVOIR qu'ils parlent à une interface de mots clés. Cela perturbe le plan de refactoring.
En Java, il est de tradition d'utiliser un meilleur modèle de dénomination: FooImple implements FooCependant, cela n'aide qu'au niveau du code source car Java compile des interfaces de mots clés vers un autre binaire. Cela signifie que lorsque vous refactorisez Foodes clients concrets en clients abstraits qui n'ont pas besoin d'un seul caractère de code modifié, vous devez encore les recompiler.
Ce sont ces BOGUES dans ces langages particuliers qui empêchent les gens de repousser l'abstrait formel jusqu'à ce qu'ils en aient vraiment besoin. Vous n'avez pas dit quelle langue vous utilisez mais comprenez qu'il y a des langues qui n'ont tout simplement pas ces problèmes.
Vous n'avez pas dit la langue que vous utilisez, je vous invite donc à analyser attentivement votre langue et votre situation avant de décider que ce seront des interfaces de mots clés partout.
Le principe YAGNI joue ici un rôle clé. Mais il en va de même "S'il vous plaît, rendez-vous difficile de me tirer dans le pied".