J'y considérais probablement une odeur de code ou même un anti-motif d'avoir une classe qui implémente 23 interfaces. S'il s'agit bien d'un anti-motif, que l'appelleriez-vous? Ou est-ce simplement juste ne pas suivre le principe de responsabilité unique?
Famille royale: Ils ne font rien de particulièrement fou, mais ils ont un milliard de titres et sont liés à la plupart des autres Royals.
Je soumets que cet anti-modèle soit nommé Jack de tous les métiers ou peut-être trop de chapeaux.
Si je devais lui donner un nom, je pense que j'appellerais cela Hydra :
Dans la mythologie grecque, l'Hydra de Lernaeen (Grec: λερναία ὕδρα) était une ancienne bête d'eau chtonique sans nom sans nom, avec des traits de reptiliennes, (comme son nom Evinces) qui possédait de nombreuses têtes - les poètes mentionnent plus de têtes que les peintres de vase pourraient La peinture, et pour chaque tête coupée, elle a grandi deux de plus - et une respiration toxique si virulente, même ses pistes étaient mortelles.
La pertinence particulière est le fait qu'il a non seulement beaucoup de têtes, mais ne cesse de croître de plus en plus d'entre eux et qu'il est impossible de tuer à cause de cela. Cela a été mon expérience avec ce type de designs; Les développeurs ne conservent que de branler de plus en plus d'interfaces jusqu'à ce qu'elle ait trop de personnes pour s'adapter à l'écran. Ensuite, il est devenu si profondément enraciné dans la conception et les hypothèses du programme qui le scinder est une perspective sans espoir (si vous essayez, vous finissent souvent par avoir besoin de plus interfaces pour combler l'écart).
Un signe précoce d'une maladie imminente due à une "hydre" est une coulée fréquente d'une interface à une autre, sans contrôle de grande mentale, et souvent à une cible qui n'a aucun sens, comme dans:
public void CreateWidget(IPartLocator locator, int widgetTypeId)
{
var partsNeeded = locator.GetPartsForWidget(widgetTypeId);
return ((IAssembler)locator).BuildWidget(partsNeeded);
}
Il est évident lorsqu'il est sorti du contexte qu'il y a quelque chose de poisson à propos de ce code, mais la probabilité que cela se produise augmente avec les "têtes", un objet grandit, car des développeurs intuitivement connaissent qu'ils traitent toujours de la même créature.
Bonne chance toujours Faire une maintenance sur un objet qui implémente 23 interfaces. Les chances de vous non causant des dommages collatéraux dans le processus sont minces à néant.
23 est juste un nombre! Dans le scénario le plus probable, il est suffisamment élevé pour l'alarme. Cependant, si nous demandons, quel est le plus grand nombre de méthodes/interfaces avant de pouvoir obtenir une étiquette comme une "anti-motif"? Est-ce 5 ou 10 ou 25 ans? Vous réalisez que ce nombre n'est vraiment pas une réponse car si 10 est bon, 11 peuvent également être - et ensuite tout entier après cela.
La vraie question concerne la complexité. Et nous devrions souligner que de longues méthodes, un nombre de méthodes ou de grande taille de la classe par toutes les mesures est en réalité [~ # ~] non [~ # ~] la définition de la complexité. Oui, un code plus grand et plus grand (nombre plus grand de méthodes) le fait difficile Pour lire et saisir un nouveau débutant. Il gère également des fonctionnalités potentiellement variées, un grand nombre d'exceptions et des algorithmes assez évolués pour différents scénarios. Cela ne signifie pas que c'est complexe - il n'est que difficile à digérer.
D'autre part, un code de taille relativement petit que l'on peut espérer lire en quelques heures de temps et dehors - peut encore être complexe. Voici quand je pense que le code est (inutilement) complexe.
Chaque morceau de sagesse de la conception orientée objet peut être placé ici pour définir le "complexe" mais je restreînerais ici pour montrer quand "tant de méthodes" est une indication de la complexité.
Connaissance mutuelle. (A.k.a coupling) Beaucoup de fois où les choses sont écrites comme des cours, nous pensons tous qu'il est "Nice" Code orienté objet. Mais l'hypothèse sur l'autre classe brise essentiellement l'encapsulation réelle nécessaire. Lorsque vous avez des méthodes "étanche" des détails profonds sur l'état interne des algorithmes - et l'application est construite avec une hypothèse clé sur l'état interne de la classe de portion.
Trop de répétitions (briser) lorsque des méthodes ayant des noms similaires, mais contradictent des noms de travail - ou contredisent avec des fonctionnalités similaires. De nombreux codes Times évoluent pour prendre en charge des interfaces légèrement différentes pour différentes applications.
Trop de rôles Lorsque la classe continue d'ajouter des fonctions secondaires et de continuer à développer davantage à mesure que des personnes comme celles-ci, seules le savoir que la classe est en effet une classe deux. Étonnamment, ces personnes commencent par authentique exigences et aucune autre classe n'existe pour le faire. Considérez ceci, il existe une transaction de classe, qui indiquent les détails de la transaction. A l'air bien jusqu'à présent, quelqu'un nécessite une conversion de format sur le "temps de transaction" (entre UTC et similaire), plus tard, les gens ajoutent des règles à vérifier si certaines choses étaient sur certaines dates de valider les transactions invalident. - Je ne vais pas écrire toute l'histoire mais éventuellement, la classe de transaction construit toute la calandre avec elle, puis les gens commencent à utiliser la partie "uniquement la partie calendrier"! C'est très complexe (imaginer) pourquoi vais-je instancier "la classe de transaction" pour avoir une fonctionnalité qu'un "calendrier" me fournirait!
((in) consistance de l'API quand je vais book_a_ticket () - Je réserve un ticket! C'est très simple, quel que soit le nombre de chèques et de processus qu'il va y arriver. Maintenant, il devient complexe lorsque le flux de temps commence à effectuer cela. En règle générale, on permettrait une "recherche" et un statut disponible possible/indisponible, puis de réduire le back-out, vous commencez à enregistrer certains pointeurs de contexte à l'intérieur du ticket an. Reportez-vous Pour réserver le ticket. La recherche n'est pas la seule fonctionnalité - les choses s'aggravent après de nombreuses "fonctionnalités latérales". Dans le processus, la signification de book_a_ticket () implique book_that_ticket ()! et ça peut être inimaginement complexe.
Il y a probablement beaucoup de telles situations que vous verriez dans un code très évolutif et je suis sûr que beaucoup de gens peuvent ajouter des scénarios, où "tant de méthodes" n'a pas de sens ni de faire ce que vous penseriez évidemment. C'est l'anti-motif.
Mon expérience personnelle est que lorsque des projets qui démarrent raisonnablement Bottom Up, beaucoup de choses pour lesquelles il aurait dû être de véritables classes sur leurs propres-es-mémoires enterrées ou pires restent encore fractionnées entre différentes classes et accroître les augmentations. Le plus souvent ce qui aurait mérité de 10 classes, mais n'a que 4 ans, il est probable que beaucoup d'entre eux ont des méthodes polyvalentes et grandes de méthodes. Appelez-le un dieu et un dragon, c'est ce qui est mauvais.
MAIS Vous rencontrez des très grandes classes qui sont soigneusement cohérentes, elles ont 30 méthodes - et sont encore très propres. Ils peuvent être bons.
Le nombre d'interfaces augmente souvent lorsqu'un objet générique est utilisé dans différents environnements. En C #, les variantes d'Icomarable, d'IequalityComparer et d'IComarer permettent de trier dans des configurations distinctes, vous pourriez donc vous mettre à la mettre en œuvre, certains d'entre eux peut-être plus d'une fois que vous pouvez implémenter les versions génériques fortement dactylographiées ainsi que les versions non génériques De plus, vous pouvez implémenter plus d'un des génériques.
Prenons un exemple de scénario, disons une boutique en ligne où vous pouvez acheter des crédits avec lesquels vous pouvez acheter autre chose (des sites de photographies utilisent souvent ce système). Vous pourriez avoir une classe "Valuta" et une classe "Credits" qui hériter de la même base. Valuta a des surcharges et des routines de comparaison des opérateurs nécessitants qui vous permettent de faire des calculs sans vous soucier de la propriété "Monnaie" (ajoutant des livres à des dollars par exemple). Les crédits sont plus simples mais ont un autre comportement distinct. Voulant pouvoir les comparer les uns avec les autres, vous pouvez finir par mettre en œuvre icomarable ainsi que icomarable et les autres variantes d'interfaces de comparaison sur les deux (même s'ils utilisent une mise en œuvre commune, que ce soit dans la classe de base ou ailleurs).
Lors de la mise en œuvre de sérialisation, iserialisable, IdesérializationCallback est mis en œuvre. Ensuite, mettre en œuvre des piles d'annulation: ICLONABLE est ajoutée. Fonctionnalité d'isdirty: iobservable, inotifyPropertyChanged. Autoriser les utilisateurs à éditer les valeurs à l'aide de chaînes: iconvertables ... La liste peut continuer et sur ...
Dans les langues modernes, nous voyons une tendance différente qui aide à séparer ces aspects et à les placer dans leurs propres cours, en dehors de la classe principale. La classe ou l'autre extérieur est ensuite associé à la classe cible à l'aide de l'annotation (attributs). Il est souvent possible de rendre les classes de l'aspect extérieur plus ou moins génériques.
L'utilisation d'attributs (annotation) est soumise à la réflexion. Un inconvénient est une perte de performance mineure (initiale). Un inconvénient (souvent émotionnel) est que des principes tels que l'encapsulation doivent être détendus.
Il y a toujours d'autres solutions, mais pour chaque solution nifty, il y a un compromis ou une prise. Par exemple, l'utilisation d'une solution ORM peut exiger que toutes les propriétés soient déclarées virtuelles. Les solutions de sérialisation peuvent exiger des constructeurs par défaut sur vos classes. Si vous utilisez une injection de dépendance, vous risquez de mettre en œuvre 23 interfaces sur une seule classe.
Dans mes yeux, 23 interfaces ne doivent pas être mauvaises par définition. Il pourrait y avoir un programme bien pensé, ou une certaine détermination de principe telle que d'éviter l'utilisation de la réflexion ou des croyances d'encapsulation extrêmes.
Chaque fois que vous passez un emploi ou devoir construire une architecture existante. Mon conseil est d'abord à connaître pleinement la connaissance, n'essayez pas de tout refroidir trop rapidement. Écoutez le développeur d'origine (s'il est toujours là) et essayez de comprendre les pensées et les idées derrière ce que vous voyez. Lorsque vous posez des questions, faites-le donc de ne pas le casser, mais d'apprendre ... Oui, tout le monde a ses propres marteaux d'or, mais plus vous pouvez collecter plus facilement avec les collègues plus faciles.
Ses sons comme pensent qu'ils ont une paire "Getter"/"Setter" pour chaque propriété, comme c'est normal pour un "haricot" typique et favorisé toutes ces méthodes à l'interface. Alors, que diriez-vous de l'appeler un "a haricot". Ou la "flatulence" plus rabelaisienne après les affectations bien connues de trop de haricots.
"Trop nombreux" est subjectif: style de programmation? performance? Conformité aux normes? des précédents? juste un sens clair-de-confort/confiance?
Tant que votre code se comporte à droite et ne présente aucun problème de maintenabilité, 23 pourrait même être la nouvelle norme. Un jour, je pourrais alors dire: "Vous pouvez faire un excellent travail avec 23 interfaces, voir: Jonas Elfström".
Je dirais que la limite devrait être un nombre plus petit que 23 - plus comme 5 ou 7,
[.
(Ainsi, N + N + Nombre d'interfaces héritées, où n <= 7.)
Si votre classe implémente trop d'interfaces, c'est probablement un classe de die .