Je lisais ce wiki sur le principe des abstractions stables (SAP) .
Le SAP déclare que plus un package est stable, plus il doit être abstrait. Cela implique que si un paquet est moins stable (plus susceptible de changer), il devrait être plus concret. Ce que je ne comprends pas vraiment, c'est pourquoi cela devrait être le cas. Sûrement dans tous les cas, quelle que soit la stabilité, nous devrions dépendre d'abstractions et cacher la mise en œuvre concrète?
design
design-patterns
architecture
object-oriented-design
design-principles
SteveCallender
la source
la source
Réponses:
Considérez vos packages comme une API, pour prendre l'exemple du papier, prenez les définitions de
Reader
avecstring Reader.Read()
etWriter
avecvoid Writer.Write(string)
comme votre API abstraite.Vous pouvez ensuite créer une classe
Copy
avec une méthodeCopier.Copy(Reader, Writer)
et la mise en œuvreWriter.Write(Reader.Read())
et peut - être certains contrôles de santé mentale.Maintenant, vous faites des implémentations concrètes, par exemple
FileReader
,FileWriter
,KeyboardReader
etDownloadThingsFromTheInternetReader
.Et si vous voulez changer votre implémentation de
FileReader
? Pas de problème, changez simplement la classe et recompilez.Et si vous voulez changer la définition de votre abstraction
Reader
? Oops, vous ne pouvez pas changer cela, mais vous devrez aussi changerCopier
,FileReader
,KeyboardReader
etDownloadThingsFromTheInternetReader
.C'est la raison d'être du principe d'abstraction stable: rendez vos concrétisations moins stables que les abstractions.
la source
À cause de YAGNI .
Si vous n'avez actuellement qu'une seule implémentation d'une chose , pourquoi vous embêter avec une couche supplémentaire et inutile? Cela ne conduira qu'à une complexité inutile. Pire encore, vous fournissez parfois une abstraction en pensant au jour où une deuxième implémentation viendra ... et ce jour ne se produit jamais. Quelle perte de travail!
Je pense aussi que la vraie question à se poser n'est pas "Dois-je dépendre des abstractions?" mais plutôt "Ai-je besoin de modularité?". Et la modularité n'est pas toujours nécessaire, voir ci-dessous.
Dans l'entreprise dans laquelle je travaille, certains des logiciels que je développe sont fortement liés à un périphérique matériel avec lequel il doit communiquer. Ces appareils sont développés pour atteindre des objectifs très spécifiques et sont tout sauf modulaires. :-) Une fois que le premier produit appareil sort de l'usine et est installé quelque part, à la fois son firmware et le matériel ne peut jamais changer, jamais .
Je peux donc être sûr que certaines parties du logiciel n'évolueront jamais. Ces parties n'ont pas besoin de dépendre d'abstractions car il n'existe qu'une seule implémentation et celle-ci ne changera jamais. Déclarer des abstractions sur ces parties de code ne fera que dérouter tout le monde et prendra plus de temps (sans produire de valeur).
la source
Je pense que vous êtes peut-être dérouté par le mot stable choisi par Robert Martin. Voici où je pense que la confusion commence:
Si vous lisez l' article original , vous verrez (c'est moi qui souligne):
J'ai toujours eu du mal avec le choix de l'auteur du mot stable , car j'ai (comme vous) tendance à penser à l'aspect "vraisemblance" de la stabilité, c'est-à-dire peu susceptible de changer . La difficulté implique que le changement de ce module cassera beaucoup d'autres modules, et cela va demander beaucoup de travail pour corriger le code.
Martin utilise également les mots indépendant et responsable , qui me donnent beaucoup plus de sens. Dans son séminaire de formation, il a utilisé une métaphore sur les parents d'enfants qui grandissent et sur la façon dont ils devraient être «responsables», car leurs enfants dépendent d'eux. Le divorce, le chômage, l'incarcération, etc. sont de grands exemples concrets de l'impact négatif que le changement de parents aura sur les enfants. Par conséquent, les parents doivent être «stables» au profit de leurs enfants. Soit dit en passant, cette métaphore des enfants / parents n'est pas nécessairement liée à l'héritage dans la POO!
Donc, suivant l'esprit de «responsable», j'ai trouvé des significations alternatives pour difficile à changer (ou ne devrait pas changer ):
Donc, brancher ces définitions dans la déclaration
Citons le principe des abstractions stables (SAP), en insistant sur les mots déroutants stable / instable:
Clarifier sans ces mots déroutants:
TL; DR
Le titre de votre question demande:
Je pense que si vous créez correctement les abstractions (par exemple, elles existent parce que beaucoup de code en dépend), alors il n'y a pas d'inconvénients importants.
la source
Les abstractions sont des choses difficiles à changer dans le logiciel car tout dépend d'elles. Si votre paquet va changer souvent et qu'il fournit des abstractions, les personnes qui en dépendent seront obligées de réécrire une grande partie de leur code lorsque vous changez quelque chose. Mais si votre package instable fournit des implémentations concrètes, beaucoup moins de code devra être réécrit après les modifications.
Donc, si votre paquet va changer souvent, il devrait mieux fournir des bétons, pas des abstractions. Sinon ... qui diable l'utilisera? ;)
la source
Gardez à l'esprit la métrique de stabilité de Martin et ce qu'il entend par «stabilité»:
Ou:
Autrement dit, un package est considéré comme complètement instable si toutes ses dépendances sont sortantes: il utilise d'autres choses, mais rien ne l'utilise. Dans ce cas, il est logique que cette chose soit concrète. Ce sera également le type de code le plus facile à modifier car rien d'autre ne l'utilise, et donc rien d'autre ne peut se casser si ce code est modifié.
Pendant ce temps, lorsque vous avez le scénario opposé de "stabilité" complète avec un package utilisé par une ou plusieurs choses, mais qu'il n'utilise rien seul, comme un package central utilisé par le logiciel, c'est à ce moment que Martin dit que cette chose devrait être abstrait. Cela est également renforcé par la partie DIP de SOLI (D), le principe d'inversion des dépendances, qui stipule essentiellement que les dépendances doivent circuler uniformément vers les abstractions pour le code de bas et de haut niveau.
C'est-à-dire que les dépendances devraient se diriger uniformément vers la "stabilité", et plus précisément, les dépendances devraient se diriger vers les packages avec plus de dépendances entrantes que les dépendances sortantes et, en outre, les dépendances devraient se diriger vers les abstractions. L'essentiel de la raison derrière cela est que les abstractions offrent une marge de manœuvre pour substituer un sous-type à un autre, offrant ce degré de flexibilité pour que les parties concrètes implémentant l'interface changent sans casser les dépendances entrantes à cette interface abstraite.
Eh bien, je suis en fait en désaccord avec Martin ici pour mon domaine au moins, et ici je dois introduire une nouvelle définition de "stabilité" comme dans "manque de raisons de changer". Dans ce cas, je dirais que les dépendances devraient se diriger vers la stabilité, mais les interfaces abstraites n'aident pas si les interfaces abstraites sont instables (par ma définition de "instable", comme sujettes à être modifiées à plusieurs reprises, pas Martin). Si les développeurs ne parviennent pas à obtenir les abstractions correctes et que les clients changent d'avis à plusieurs reprises de manière à rendre les tentatives abstraites de modélisation du logiciel incomplètes ou inefficaces, alors nous ne bénéficions plus de la flexibilité améliorée des interfaces abstraites pour protéger le système contre les changements en cascade qui brisent les dépendances . Dans mon cas personnel, j'ai trouvé des moteurs ECS, tels que ceux trouvés dans les jeux AAA,le plus concret : vers les données brutes, mais ces données sont très stables (comme dans "peu susceptibles de devoir être modifiées"). J'ai souvent trouvé la probabilité que quelque chose nécessitant des changements futurs soit une mesure plus utile que le rapport des couplages efférents au total pour guider les décisions SE.
Donc, je modifierais un peu DIP et je dirais simplement que "les dépendances devraient se diriger vers les composants qui ont la plus faible probabilité de nécessiter d'autres modifications", que ces composants soient des interfaces abstraites ou des données brutes. Tout ce qui m'importe, c'est la probabilité qu'elles nécessitent des changements directs de conception. Les abstractions ne sont utiles dans ce contexte de stabilité que si quelque chose, en étant abstrait, réduit cette probabilité.
Dans de nombreux contextes, ce pourrait être le cas avec des ingénieurs et des clients décents qui anticipent les besoins du logiciel à l'avance et conçoivent des abstractions stables (comme dans, immuables), tandis que ces abstractions leur offrent toute la marge de manœuvre dont elles ont besoin pour échanger des implémentations concrètes. Mais dans certains domaines, les abstractions peuvent être instables et sujettes à être inadéquates, tandis que les données requises du moteur peuvent être beaucoup plus faciles à anticiper et à stabiliser à l'avance. Dans ces cas, il peut donc être plus avantageux du point de vue de la maintenabilité (la facilité de changer et d'étendre le système) que les dépendances se dirigent vers les données plutôt que vers les abstractions. Dans un ECS, les parties les plus instables (comme dans les pièces les plus fréquemment modifiées) sont généralement les fonctionnalités résidant dans les systèmes (
PhysicsSystem
, par exemple), tandis que les parties les plus stables (comme celles qui sont le moins susceptibles d'être modifiées) sont les composants qui ne sont constitués que de données brutes (MotionComponent
, par exemple) que tous les systèmes utilisent.la source