Découplage des classes de l'interface utilisateur

27

Quelle est la meilleure pratique quand il s'agit d'écrire des classes qui pourraient avoir besoin de connaître l'interface utilisateur. Une classe sachant comment se dessiner ne briserait-elle pas certaines des meilleures pratiques car cela dépend de ce qu'est l'interface utilisateur (console, GUI, etc.)?

Dans de nombreux livres de programmation, je suis tombé sur l'exemple "Shape" qui montre l'héritage. La forme de classe de base a une méthode draw () que chaque forme telle qu'un cercle et un carré remplace. Cela permet le polymorphisme. Mais la méthode draw () ne dépend-elle pas beaucoup de l'interface utilisateur? Si nous écrivons cette classe pour, disons, Win Forms, nous ne pouvons pas la réutiliser pour une application console ou une application Web. Est-ce correct?

La raison de la question est que je me retrouve toujours coincé et que je raccroche à la façon de généraliser les classes afin qu'elles soient les plus utiles. Cela fonctionne en fait contre moi et je me demande si j'essaye trop fort.

Pete
la source
Pourquoi voulez-vous vous découpler? Parce que vous avez entendu que c'était la bonne chose à faire ou avez-vous d'autres raisons?
SoylentGray
2
Toute la «classe sachant se dessiner» n'est qu'un exemple horrible et ancien que je souhaiterais voir disparaître. Surtout sur la pile du programmeur de jeu =)
Patrick Hughes
2
@Chad Je n'ai pas vraiment beaucoup d'expérience en dehors de l'école. J'ai lu des livres et j'aime vraiment apprendre et lire de nouvelles choses sur les modèles de conception et les meilleures pratiques. Alors oui, vous pouvez dire que j'ai entendu dire que le découplage est bon, mais cela a aussi du sens. Je veux écrire du code que je peux utiliser pour une application WinForms de bureau, par exemple, puis prendre ce code et le réutiliser autant que possible pour un site Web ou même une application Silverlight.
Pete
@Pete - C'est une bonne réponse.
SoylentGray
2
@Patrick: pour être honnête, si vous écrivez une Shapeclasse, vous écrivez probablement la pile graphique elle-même, et non pas un client dans la pile graphique.
Ken Bloom

Réponses:

13

Quelle est la meilleure pratique quand il s'agit d'écrire des classes qui pourraient avoir besoin de connaître l'interface utilisateur. Une classe sachant comment se dessiner ne briserait-elle pas certaines des meilleures pratiques car cela dépend de ce qu'est l'interface utilisateur (console, GUI, etc.)?

Cela dépend de la classe et du cas d'utilisation. Un élément visuel sachant se dessiner n'est pas nécessairement une violation du principe de responsabilité unique.

Dans de nombreux livres de programmation, je suis tombé sur l'exemple "Shape" qui montre l'héritage. La forme de classe de base a une méthode draw () que chaque forme telle qu'un cercle et un carré remplace. Cela permet le polymorphisme. Mais la méthode draw () ne dépend-elle pas beaucoup de l'interface utilisateur?

Encore une fois, pas nécessairement. Si vous pouvez créer une interface (drawPoint, drawLine, définir la couleur, etc.), vous pouvez à peu près passer n'importe quel contexte pour dessiner des choses sur quelque chose à la forme, par exemple dans le constructeur de la forme. Cela permettrait aux formes de se dessiner sur une console ou une toile donnée.

Si nous écrivons cette classe pour, disons, Win Forms, nous ne pouvons pas la réutiliser pour une application console ou une application Web. Est-ce correct?

Eh bien, c'est vrai. Si vous écrivez un UserControl (pas une classe en général) pour Windows Forms, vous ne pourrez pas l'utiliser avec une console. Mais ce n'est pas un problème. Pourquoi vous attendriez-vous à ce qu'un UserControl pour Windows Forms fonctionne avec tout type de présentation? UserControl doit faire une chose et bien le faire. C'est lié à une certaine forme de présentation par définition. Au final, l'utilisateur a besoin de quelque chose de concret et non d'une abstraction. Cela n'est peut-être que partiellement vrai pour les frameworks, mais pour les applications d'utilisateur final, c'est le cas.

Cependant, la logique sous-jacente doit être découplée, afin que vous puissiez l'utiliser à nouveau avec d'autres technologies de présentation. Introduisez des interfaces si nécessaire pour maintenir l'orthogonalité de votre application. La règle générale est la suivante: les choses concrètes doivent être échangeables avec d'autres choses concrètes.

La raison de la question est que je me retrouve toujours coincé et que je raccroche à la façon de généraliser les classes afin qu'elles soient les plus utiles. Cela fonctionne en fait contre moi et je me demande si j'essaye trop fort.

Vous savez, les programmeurs extrêmes aiment leur attitude YAGNI . N'essayez pas de tout écrire de manière générique et n'essayez pas trop en essayant de tout faire à usage général. Cela s'appelle une ingénierie excessive et conduira finalement à un code totalement alambiqué. Donnez à chaque composant exactement une tâche et assurez-vous qu'il le fait bien. Mettez des abstractions si nécessaire, là où vous vous attendez à ce que les choses changent (par exemple, interface pour dessiner le contexte, comme indiqué ci-dessus).

En général, lors de l'écriture d'applications métier, vous devez toujours essayer de découpler les choses. MVC et MVVM sont parfaits pour découpler la logique de la présentation, vous pouvez donc la réutiliser pour une présentation Web ou une application console. N'oubliez pas qu'en fin de compte, certaines choses doivent être concrètes. Vos utilisateurs ne peuvent pas travailler avec une abstraction, ils ont besoin de quelque chose de concret. Les abstractions ne sont que des aides pour vous, le programmeur, pour garder le code extensible et maintenable. Vous devez savoir où vous avez besoin de votre code pour être flexible. Finalement, toutes les abstractions doivent donner naissance à quelque chose de concret.

Edit: Si vous voulez en savoir plus sur les techniques d'architecture et de conception qui peuvent fournir les meilleures pratiques, je vous suggère de lire la réponse @Catchops et de lire sur les pratiques SOLID sur wikipedia.

De plus, pour commencer, je recommande toujours le livre suivant: Head First Design Patterns . Il vous aidera à comprendre les techniques d'abstraction / les pratiques de conception OOP, plus encore que le livre GoF (qui est excellent, il ne convient tout simplement pas aux débutants).

Faucon
la source
1
Bonne réponse, mais les programmeurs extrêmes ne «mettent pas dans des abstractions où nous nous attendons à ce que les choses changent». Nous mettons dans les abstractions où les choses sont en train de changer, pour tarir le code.
kevin cline
1
@kevin cline c'est génial à moins que vous ne conceviez une bibliothèque accessible au public avec une API qui doit se conformer au comportement et aux interfaces précédents.
Magnus Wolffelt
@Magnus - oui, la conception d'une bibliothèque dans certaines langues, la planification de la compatibilité binaire en amont, est délicate. On est obligé d'écrire toutes sortes de code actuellement inutile pour permettre une future extension. Cela peut être raisonnable pour les langues qui se compilent dans le métal. C'est idiot pour les langages qui se compilent sur une machine virtuelle.
kevin cline
19

Vous avez certainement raison. Et non, vous "essayez très bien" :)

En savoir plus sur le principe de responsabilité unique

Votre fonctionnement interne de la classe et la façon dont ces informations doivent être présentées à l'utilisateur sont deux responsabilités.

N'ayez pas peur de découpler les cours. Rarement le problème est trop d'abstraction et de découplage :)

Deux modèles très pertinents sont Model – view – controller pour les applications Web et Model View ViewModel pour Silverlight / WPF.

Boris Yankov
la source
1
+1 Vous pouvez même utiliser MVC pour des applications non Web, tout au moins cela vous aide à penser en termes qui gardent les responsabilités claires.
Patrick Hughes
MVVM est silimar pour MVC. MVC est principalement destiné aux applications sans état telles que les applications Web.
Boris Yankov
3
-1 D'après mon expérience, l'un des problèmes les plus courants est la généralisation prématurée et la suringénierie en général.
dietbuddha
3
Vous devez d'abord savoir comment concevoir quelque chose afin de le sur-concevoir. D'après mon expérience, les gens «sous-ingénieur» logiciel beaucoup plus souvent.
Boris Yankov
Bien que j'apprécie les efforts visant à améliorer la conception de logiciels, substituer un appel sur une interface à un appel sur une classe ne dissocie rien sémantiquement. Considérez ce qui se passe lors de l'utilisation d'un langage typé dynamiquement. Il y a beaucoup trop d'accent sur le découplage superficiel (syntaxique), et peu sur le vrai découplage, dans les conseils fournis dans la section programmeurs de l'échange de pile.
Frank Hileman
7

J'utilise beaucoup MVVM et à mon avis, une classe d'objets métier ne devrait jamais avoir besoin de connaître quoi que ce soit sur l'interface utilisateur. Bien sûr, ils pourraient avoir besoin de connaître le SelectedItem, ou IsChecked, ou IsVisible, etc., mais ces valeurs n'ont pas besoin d'être liées à une interface utilisateur particulière et peuvent être des propriétés génériques de la classe.

Si vous devez faire quelque chose à l'interface dans le code derrière, comme définir le focus, exécuter une animation, gérer les raccourcis clavier, etc., le code doit faire partie du code derrière de l'interface utilisateur, pas vos classes de logique métier.

Je dirais donc, n'arrêtez pas d'essayer de séparer votre interface utilisateur et vos classes. Plus ils sont découplés, plus ils sont faciles à entretenir et à tester.

Rachel
la source
5

Il existe un certain nombre de modèles de conception éprouvés qui ont été développés au fil des ans pour répondre exactement à ce dont vous parlez. D'autres réponses à votre question ont fait référence au principe de responsabilité unique - qui est absolument valable - et à ce qui semble motiver votre question. Ce principe stipule simplement qu'une classe doit bien faire une chose. En d'autres termes, augmenter la cohésion et abaisser le couplage, c'est ce qu'est une bonne conception orientée objet - une classe fait-elle bien une chose, et n'a pas beaucoup de dépendances sur les autres.

Eh bien ... vous avez raison de remarquer que si vous voulez dessiner un cercle sur un iPhone, ce sera différent que d'en dessiner un sur un PC sous Windows. Vous DEVEZ avoir (dans ce cas) une classe concrète qui dessine un puits sur l'iPhone, et une autre qui dessine un puits sur un PC. C'est là que le contenu OO de l'héritage de base que tous ces exemples de formes décompose. Vous ne pouvez tout simplement pas le faire avec l'héritage seul.

C'est là qu'interviennent les interfaces - comme l' indique le livre Gang of Four (DOIT LIRE) - Toujours privilégier l'implémentation plutôt que l'héritage. En d'autres termes, utilisez des interfaces pour reconstituer une architecture qui peut exécuter diverses fonctions de nombreuses manières sans compter sur des dépendances codées en dur.

J'ai vu se référer aux principes SOLIDES . C’est génial. Le «S» est le principe de responsabilité unique. MAIS, le «D» signifie Dependency Inversion. Le modèle d'inversion de contrôle (injection de dépendance) peut être utilisé ici. Il est très puissant et peut être utilisé pour répondre à la question de savoir comment concevoir un système capable de dessiner un cercle pour un iPhone ainsi qu'un cercle pour le PC.

Il est possible de créer une architecture qui contient des règles métier et un accès aux données communs, mais avec différentes implémentations d'interfaces utilisateur utilisant ces constructions. Cela aide vraiment, cependant, d'avoir réellement fait partie d'une équipe qui l'a mis en œuvre et de le voir en action pour vraiment le comprendre.

Ceci est juste une réponse rapide de haut niveau à une question qui mérite une réponse plus détaillée. Je vous encourage à approfondir ces modèles. Une implémentation plus concrète de ces patters peut être trouvée sous les noms bien connus de MVC et MVVM.

Bonne chance!

Catchops
la source
J'adore quand quelqu'un downvote quelque chose sans dire pourquoi (sarcasme, bien sûr). Les principes que j'ai énoncés sont vrais - le votant se lève-t-il s'il vous plaît pour dire pourquoi.
Catchops
2

Quelle est la meilleure pratique quand il s'agit d'écrire des classes qui pourraient avoir besoin de connaître l'interface utilisateur.

Une classe sachant comment se dessiner ne briserait-elle pas certaines des meilleures pratiques car cela dépend de ce qu'est l'interface utilisateur (console, GUI, etc.)?

Dans ce cas, vous pouvez toujours utiliser MVC / MVVM et injecter différentes implémentations d'interface utilisateur à l'aide d'une interface commune:

public interface IAgnosticChartDrawing
{
   public void Draw(ChartProperties chartProperties);
   event EventHandler ChartPanned;
}

public class GuiChartDrawer : UserControl, IAgnosticChartDrawing
{
    public void Draw(ChartProperties chartProperties)
    {
        //GDI, GTK or something else...
    }

    //Implement event based on mouse actions
}

public class ConsoleChartDrawer : IAgnosticChartDrawing
{
    public void Draw(ChartProperties chartProperties)
    {
        //'Draw' using characters and symbols...
    }

    //Implement event based on keyboard actions
}

IAgnosticChartDrawing guiView = new GuiChartDrawer();
IAgnosticChartDrawing conView = new ConsoleChartDrawer();

Model model = new FinancialModel();

SampleController controllerGUI = new SampleController(model, guiView);
SampleController controllerConsole = new SampleController(model, conView);

De cette façon, vous pourrez réutiliser votre logique de contrôleur et de modèle tout en étant capable d'ajouter de nouveaux types de GUI.

Tanière
la source
2

Il existe différents modèles pour ce faire: MVP, MVC, MVVM, etc ...

Un bel article de Martin Fowler (grand nom) à lire est Architectures GUI: http://www.martinfowler.com/eaaDev/uiArchs.html

MVP n'a pas encore été mentionné mais il mérite certainement d'être cité: jetez-y un œil.

C'est le modèle suggéré par les développeurs de Google Web Toolkit, c'est vraiment bien.

Vous pouvez trouver du vrai code, des exemples réels et des raisons pour lesquelles cette approche est utile ici:

http://code.google.com/webtoolkit/articles/mvp-architecture.html

http://code.google.com/webtoolkit/articles/mvp-architecture-2.html

L'un des avantages de suivre cette approche ou des approches similaires qui n'a pas été suffisamment souligné ici est la testabilité! Dans de nombreux cas, je dirais que c'est le principal avantage!

Andrea Zilio
la source
1
+1 pour les liens. Je lisais un article similaire sur MSDN à propos de MVVM et ces articles Google sont bien meilleurs, mais sur un modèle légèrement différent.
Pete
1

C'est l'un des endroits où la POO ne parvient pas à faire un bon travail d'abstraction. Le polymorphisme OOP utilise la répartition dynamique sur une seule variable («ceci»). Si le polymorphisme était enraciné dans Shape, vous ne pouvez pas répartir polymorphiquement sur le moteur de rendu (console, interface graphique, etc.).

Considérons un système de programmation qui pourrait répartir sur deux ou plusieurs variables:

poly_draw(Shape s, Renderer r)

et supposons également que le système pourrait vous donner un moyen d'exprimer poly_draw pour diverses combinaisons de types Shape et de types Renderer. Il serait alors facile de trouver une classification des formes et des rendus, non? Le vérificateur de type vous aiderait en quelque sorte à déterminer s'il y avait des combinaisons de formes et de rendus que vous avez peut-être manqué d'implémenter.

La plupart des langages POO ne prennent en charge rien de tel que ci-dessus (certains le font, mais ils ne sont pas courants). Je vous suggère de jeter un œil au modèle de visiteur pour une solution de contournement.

ritesh
la source
1

... la méthode draw () ne dépend-elle pas beaucoup de l'interface utilisateur? Si nous écrivons cette classe pour, disons, Win Forms, nous ne pouvons pas la réutiliser pour une application console ou une application Web. Est-ce correct?

Les sons ci-dessus me conviennent. À ma connaissance, on peut dire que cela signifie un couplage relativement étroit entre le contrôleur et la vue en termes de modèle de conception MVC. Cela signifie également que pour basculer entre desktop-console-webapp, il faudra en conséquence basculer à la fois Controller et View en tant que paire - seul le modèle reste inchangé.

... Je me retrouve toujours coincé et raccroché sur la façon de généraliser les classes afin qu'elles soient les plus utiles.

Eh bien, mon point de vue actuel est que ce couplage View-Controller dont nous parlons est OK et encore plus, il est plutôt tendance dans un design moderne.

Cependant, il y a un an ou deux, je n'étais pas aussi sûr de cela. J'ai changé d'avis après avoir étudié les discussions du forum Sun sur les modèles et la conception OO.

Si vous êtes intéressé, essayez ce forum vous-même - il a migré vers Oracle maintenant ( lien ). Si vous y arrivez, essayez de cingler le gars Saish - à l'époque, ses explications sur ces questions délicates m'ont été très utiles. Je ne peux pas dire s'il participe toujours - moi-même, je n'y suis pas depuis longtemps

moucheron
la source
1

Mais la méthode draw () ne dépend-elle pas beaucoup de l'interface utilisateur?

D'un point de vue pragmatique, du code dans votre système doit savoir comment dessiner quelque chose comme un Rectanglesi c'est une exigence de l'utilisateur. Et cela va se résumer à un moment donné à faire des choses de très bas niveau comme la pixellisation des pixels ou l'affichage de quelque chose dans une console.

Du point de vue du couplage, la question est de savoir qui / quoi devrait dépendre de ce type d'informations et à quel degré de détail (dans quelle mesure, par exemple, abstrait)?

Résumé des capacités de dessin / rendu

Parce que si le code de dessin de niveau supérieur ne dépend que de quelque chose de très abstrait, cette abstraction pourrait fonctionner (par substitution d'implémentations concrètes) sur toutes les plates-formes que vous avez l'intention de cibler. À titre d'exemple artificiel, une IDrawerinterface très abstraite peut être capable d'être implémentée dans la console et les API GUI pour faire des choses comme des formes de tracé (l'implémentation de la console peut traiter la console comme une "image" 80xN avec l'art ASCII). Bien sûr, c'est un exemple artificiel car ce n'est généralement pas ce que vous voulez faire, c'est traiter une sortie de console comme un tampon image / frame; généralement, la plupart des besoins des utilisateurs exigent davantage d'interactions textuelles dans les consoles.

Une autre considération est à quel point est-il facile de concevoir une abstraction stable? Parce que cela pourrait être facile si tout ce que vous visez est des API GUI modernes pour résumer les capacités de base de dessin de formes comme les lignes de tracé, les rectangles, les chemins, le texte, les choses de ce genre (juste une rastérisation 2D simple d'un ensemble limité de primitives) , avec une interface abstraite qui peut être facilement implémentée pour eux tous à travers différents sous-types à peu de frais. Si vous pouvez concevoir une telle abstraction efficacement et l'implémenter sur toutes les plates-formes cibles, je dirais que c'est un mal bien moindre, voire un mal du tout, pour une forme ou un contrôle graphique ou quoi que ce soit pour savoir comment se dessiner en utilisant un tel abstraction.

Mais disons que vous essayez d'abstraire les détails sanglants qui varient entre une Playstation Portable, un iPhone, une XBox One et un PC de jeu puissant tandis que vos besoins sont d'utiliser les techniques de rendu / d'ombrage 3D en temps réel les plus avancées sur chacun . Dans ce cas, essayer de proposer une interface abstraite pour résumer les détails de rendu lorsque les capacités matérielles et les API sous-jacentes varient si énormément est presque certain de générer un temps énorme de conception et de reconception, une forte probabilité de modifications de conception récurrentes avec des imprévus découvertes, et également une solution de dénominateur commun le plus bas qui ne parvient pas à exploiter le caractère unique et la puissance du matériel sous-jacent.

Faire passer les dépendances vers des conceptions stables et "faciles"

Dans mon domaine, je suis dans ce dernier scénario. Nous ciblons de nombreux matériels différents avec des capacités et des API sous-jacentes radicalement différentes, et essayer de proposer une seule abstraction de rendu / dessin pour les gouverner tous est sans espoir (nous pourrions devenir mondialement célèbre en le faisant efficacement car ce serait un jeu). changeur dans l'industrie). Donc, la dernière chose que je veux dans mon cas est comme l'analogique Shapeou Modelou Particle Emitterqui sait se dessiner, même s'il exprime ce dessin de la manière la plus élevée et la plus abstraite possible ...

... parce que ces abstractions sont trop difficiles à concevoir correctement, et quand un design est difficile à obtenir correctement, et que tout en dépend, c'est une recette pour les changements de conception centrale les plus coûteux qui ondulent et cassent tout en fonction. Donc, la dernière chose que vous voulez, c'est que les dépendances de vos systèmes se dirigent vers des conceptions abstraites trop difficiles à obtenir (trop difficiles à stabiliser sans changements intrusifs).

Difficile dépend de facile, pas facile dépend de difficile

Donc, ce que nous faisons à la place, c'est que les dépendances se dirigent vers des choses faciles à concevoir. Il est beaucoup plus facile de concevoir un "modèle" abstrait qui se concentre uniquement sur le stockage de choses comme les polygones et les matériaux et d'obtenir ce design correct que de concevoir un "rendu" abstrait qui peut être efficacement mis en œuvre (via des sous-types concrets substituables) pour dessiner le dessin. demande uniformément un matériel aussi disparate qu'une PSP à partir d'un PC.

entrez la description de l'image ici

Nous inversons donc les dépendances loin des choses difficiles à concevoir. Au lieu de faire en sorte que les modèles abstraits sachent se dessiner sur un design de rendu abstrait dont ils dépendent tous (et casser leurs implémentations si ce design change), nous avons plutôt un rendu abstrait qui sait comment dessiner chaque objet abstrait de notre scène ( modèles, émetteurs de particules, etc.), et ainsi nous pouvons ensuite implémenter un sous-type de rendu OpenGL pour les PC comme RendererGl, un autre pour les PSP comme RendererPsp, un autre pour les téléphones mobiles, etc. Dans ce cas, les dépendances se dirigent vers des conceptions stables, faciles à corriger, du rendu à différents types d'entités (modèles, particules, textures, etc.) dans notre scène, et non l'inverse.

entrez la description de l'image ici

  • J'utilise "stabilité / instabilité" dans un sens légèrement différent de la métrique des couplages afférents / efférents de l'oncle Bob qui mesure plus la difficulté du changement pour autant que je puisse comprendre. Je parle plus de "probabilité d'exiger un changement", bien que sa métrique de stabilité y soit utile. Lorsque la «probabilité de changement» est proportionnelle à la «facilité de changement» (ex: les éléments les plus susceptibles de nécessiter des changements présentent l'instabilité la plus élevée et les couplages afférents de la métrique de l'oncle Bob), alors de tels changements probables sont bon marché et non intrusifs à effectuer. , ne nécessitant que le remplacement d'une implémentation sans toucher à aucune conception centrale.

Si vous essayez d'abstraire quelque chose au niveau central de votre base de code et que c'est trop difficile à concevoir, au lieu de vous battre obstinément la tête contre les murs et d'y apporter constamment des changements intrusifs chaque mois / année, ce qui nécessite la mise à jour de 8000 fichiers source car c'est briser tout ce qui en dépend, ma suggestion numéro un est d'envisager d'inverser les dépendances. Voyez si vous pouvez écrire le code d'une manière telle que la chose qui est si difficile à concevoir dépend de tout le reste qui est plus facile à concevoir, ne pas avoir les choses qui sont plus faciles à concevoir en fonction de la chose qui est si difficile à concevoir. Notez que je parle de conceptions (en particulier de conceptions d'interfaces) et non d'implémentations: parfois les choses sont faciles à concevoir et difficiles à implémenter, et parfois les choses sont difficiles à concevoir mais faciles à mettre en œuvre. Les dépendances se déplacent vers les conceptions, donc l'accent ne devrait être mis que sur la difficulté de concevoir quelque chose ici pour déterminer la direction dans laquelle les dépendances se déplacent.

Principe de responsabilité unique

Pour moi, SRP n'est pas si intéressant ici habituellement (bien que cela dépende du contexte). Je veux dire qu'il y a un acte d'équilibre sur la corde raide dans la conception de choses claires dans leur objectif et maintenables, mais vos Shapeobjets peuvent avoir à exposer des informations plus détaillées s'ils ne savent pas comment se dessiner, par exemple, et il peut ne pas y avoir beaucoup de choses significatives à faire avec une forme dans un contexte d'utilisation particulier que de la construire et de la dessiner. Il y a des compromis avec à peu près tout, et ce n'est pas lié à la SRP qui peut rendre les choses conscientes de la façon de se dessiner capables de devenir un tel cauchemar de maintenance dans mon expérience dans certains contextes.

Cela a beaucoup plus à voir avec le couplage et la direction dans laquelle les dépendances circulent dans votre système. Si vous essayez de porter une interface de rendu abstraite dont tout dépend (car ils l'utilisent pour se dessiner) vers une nouvelle API / matériel cible et que vous réalisez que vous devez modifier considérablement sa conception pour qu'elle fonctionne efficacement là-bas, alors c'est un changement très coûteux à effectuer qui nécessite de remplacer les implémentations de tout ce qui sait se dessiner dans votre système. Et c'est le problème de maintenance le plus pratique que je rencontre avec des choses qui savent comment se dessiner si cela se traduit par une charge de dépendances qui coule vers des abstractions qui sont trop difficiles à concevoir correctement à l'avance.

Développeur Pride

Je mentionne ce point parce que, selon mon expérience, c'est souvent le plus grand obstacle à la direction des dépendances vers des choses plus faciles à concevoir. Il est très facile pour les développeurs de devenir un peu ambitieux ici et de dire: "Je vais concevoir l'abstraction de rendu multiplateforme pour les gouverner tous, je vais résoudre ce que les autres développeurs passent des mois à porter, et je vais obtenir il va bien et ça va fonctionner comme par magie sur chaque plate-forme unique que nous prenons en charge et utiliser les techniques de rendu de pointe sur chacun; je l'ai déjà imaginé dans ma tête. "Dans ce cas, ils résistent à la solution pratique qui consiste à éviter de faire cela et à inverser simplement la direction des dépendances et à traduire ce qui pourrait être extrêmement coûteux et les modifications récurrentes de la conception centrale en des changements récurrents simplement bon marché et locaux à la mise en œuvre. Il doit y avoir une sorte d'instinct de "drapeau blanc" chez les développeurs pour abandonner quand quelque chose est trop difficile à concevoir à un niveau aussi abstrait et reconsidérer toute leur stratégie, sinon ils sont dans une situation de chagrin et de douleur. Je suggérerais de transférer de telles ambitions et un esprit combatif vers des implémentations de pointe d'une chose plus facile à concevoir que de prendre de telles ambitions de conquête du monde au niveau de la conception d'interface.

Dragon Energy
la source
1
Je n'arrive pas à saisir l'idée de "inverser les dépendances loin des choses difficiles à concevoir", parlez-vous uniquement d'héritage? En utilisant l'exemple de PSP / OpenGL, vous voulez dire qu'au lieu de faire un OpenGLBillboard, vous feriez un OpenGLRendererqui sait dessiner n'importe quel type de IBillBoard? Mais le ferait-il en déléguant la logique à IBillBoard, ou aurait-il d'énormes commutateurs ou IBillBoardtypes avec des conditions? C'est ce que je trouve difficile à comprendre, car cela ne semble pas du tout maintenable!
Steve Chamaillard
@SteveChamaillard La différence est, disons, que PSP (matériel limité) doit gérer les primitives de volume en utilisant la transparence du screendoor à l'ancienne et un ordre différent d'évaluation du rendu: digitalrune.github.io/DigitalRune-Documentation/media/… . Lorsque vous avez un central RendererPspqui connaît les abstractions de haut niveau de votre scène de jeu, il peut alors faire toute la magie et les backflips dont il a besoin pour rendre ces choses d'une manière qui semble convaincante sur la PSP ....
Dragon Energy
Alors que si vous aviez de telles primitives de volume demandant à se rendre elles-mêmes, soit leurs opérations sont si élevées qu'elles se spécifient essentiellement à rendre (ce qui ne fait aucune différence, sauf la redondance du rond-point et le couplage supplémentaire), ou l'abstraction du rendu est incapable de vraiment faire les choses de la manière la plus efficace possible sur la PSP (au moins sans faire de backflips, ce qu'il n'aurait pas besoin de faire si les dépendances étaient inversées).
Dragon Energy
1
Ok je pense que je l'ai maintenant. Fondamentalement, vous dites que ces préoccupations (c'est-à-dire le matériel) sont si élevées qu'il BillBoardserait vraiment difficile de faire des objets de bas niveau tels que ceux qui en dépendent? Alors qu'un IRendererniveau est déjà élevé et pourrait dépendre de ces préoccupations avec beaucoup moins de difficultés?
Steve Chamaillard
1
Avec des problèmes de rendu de pointe sur des plates-formes disparates, des capacités matérielles disparates, il est trop difficile de concevoir quelque chose où je suis le patron et de lui dire quoi faire. C'est beaucoup plus facile pour moi de dire: "Hé, je suis une chose trouble / brumeuse avec des particules aqueuses grisâtres comme celle-ci, et essayez de me faire bien paraître s'il vous plaît, ne capturez pas mon cou que je n'ai pas rasé récemment", et laissez le moteur de rendu comprendre comment me rendre d'une manière aussi belle et en temps réel que possible compte tenu des limites avec lesquelles il fonctionne. Toute tentative de lui dire quoi faire entraînera presque certainement une voie contre-productive.
Dragon Energy