Comment éviter au mieux l'écriture de code graphique gonflé?


48

Je trouve que chaque fois que je travaille avec du code GUI, le code a tendance à gonfler plus rapidement que les autres types de code. Il semble également plus difficile de refactoriser. Alors que dans d'autres types de code, je peux refactoriser assez facilement - je trouve que je peux décomposer une classe plus grande en fonctionnalités plus petites - avec la plupart des frameworks d'interface graphique, je suis souvent lié à un framework qui requiert mon widget / control / toute classe pour implémenter beaucoup plus de choses plus directement dans le widget / contrôle / que ce soit. Parfois, cela est dû à la nécessité de (a) hériter d'un widget / contrôle / chose de base ou (b) d'accéder à des méthodes protégées.

Je dois aussi généralement, par exemple, répondre à une grande variété d'entrées via des signaux / événements / quel que soit le cadre pour implémenter tous les modes d'interaction avec l'utilisateur. Je pourrais avoir besoin dans un widget / contrôle d'interface graphique pour gérer une grande variété d'entrées / sorties pouvant inclure:

  1. un clic droit / menu contextuel
  2. réagir aux sélections du menu contextuel - qui peuvent être nombreuses
  3. un moyen spécial de peindre l'interface graphique
  4. réagir à la saisie au clavier
  5. boutons, cases à cocher,
  6. etc

... tout en gérant les classes sous l'interface graphique représentant la logique métier.

Une interface graphique simple et simple peut faire croître son code assez rapidement, même en séparant la logique de commerce et en utilisant MVC, je trouve que le code d'interface graphique est un puissant aimant pour le changement.

Existe-t-il un moyen de gérer le code de l'interface graphique de manière saine et d'éviter de le laisser devenir une fenêtre cassée? Ou une masse de gestionnaires d'événements aléatoires / méthodes surchargées est-elle vraiment la meilleure solution pour le code d'interface graphique?


4
Quelle est votre définition exacte de "gonfler"?

Réponses:


36

Ce qu'il faut retenir du code d'interface graphique, c'est qu'il est piloté par les événements et que le code piloté par les événements aura toujours l'apparence d'une masse de gestionnaires d'événements organisés de manière aléatoire. Cela devient vraiment désordonné lorsque vous essayez d'introduire du code non événementiel dans la classe. Bien sûr, il a l’apparence de fournir un support pour les gestionnaires d’événements et vous pouvez garder vos gestionnaires d’événements sympas et petits, mais tout ce code de support supplémentaire flottant rend votre source d’interface graphique apparemment gonflée et désordonnée.

Alors, que pouvez-vous faire à ce sujet et comment rendre les choses plus faciles à refactoriser? Eh bien, je changerais d'abord ma définition du refactoring de quelque chose que je fais occasionnellement à quelque chose que je fais continuellement pendant que je code. Pourquoi? Parce que vous souhaitez que le refactoring vous permette de modifier votre code plus facilement, et non l'inverse. Je ne vous demande pas simplement de changer la sémantique ici, mais plutôt de faire un peu de gymnastique mentale afin de voir votre code différemment.

Les trois techniques de refactoring que j’utilise le plus souvent sont Rename , Extract Method et Extract Class . Si je n'avais jamais appris un autre refactoring, ces trois me permettraient toujours de garder mon code propre et bien structuré, et d'après le contenu de votre question, il me semble que vous allez probablement vous retrouver à utiliser les trois mêmes refactorings presque constamment garder votre code graphique mince et propre.

Vous pouvez obtenir la meilleure séparation possible entre la GUI et la logique métier dans le monde, mais le code de la GUI peut sembler comme si une mine de code avait explosé au milieu de celle-ci. Mon conseil est qu'il ne fait pas mal d'avoir une classe supplémentaire ou deux pour vous aider à gérer votre interface graphique correctement, et cela ne doit pas nécessairement être vos classes View si vous appliquez le modèle MVC - bien que vous trouviez souvent les classes intermédiaires sont si semblables à votre vision que vous ressentirez souvent le besoin de les fusionner pour plus de commodité. Mon point de vue est qu’il n’est pas très utile d’ajouter une couche supplémentaire spécifique à l’interface graphique pour gérer toute la logique visuelle, mais vous voudrez probablement en peser les avantages et les coûts.

Mon conseil est donc:

  • Ne faites rien directement derrière votre interface graphique, sauf pour invoquer et définir le mode de connexion de l'interface graphique à la vue (ou à une couche intermédiaire).
  • N'essayez pas d'aligner toutes les vues liées dans une même classe - ou même une seule classe par fenêtre graphique - à moins que cela ne soit logique. Votre alternative est de créer beaucoup de petites classes faciles à gérer pour gérer votre logique d'interface graphique.
  • Lorsque vos méthodes commencent à paraître un peu plus volumineuses qu’un code de 4 à 5 lignes, examinez si cela est nécessaire et s’il est possible d’extraire une ou deux méthodes afin que vos méthodes restent minces, même si cela signifie une classe. avec beaucoup plus de méthodes.
  • Si vos classes commencent à paraître vraiment volumineuses, commencez par supprimer TOUTES les fonctionnalités dupliquées, puis voyez si vous pouvez grouper logiquement vos méthodes de manière à en extraire une ou deux autres classes.
  • Pensez à refactoriser chaque fois que vous écrivez une ligne de code. Si vous faites fonctionner une ligne de code, voyez si vous pouvez la refactoriser pour éviter la duplication des fonctionnalités, ou pour la rendre un peu plus légère sans changer le comportement.
  • Acceptez l'inévitable, à savoir que vous aurez toujours le sentiment qu'une partie ou une autre de votre système commencera à se sentir un peu gonflée, en particulier si vous négligez de refactoriser en cours de route. Même avec une base de code bien factorisée, vous pouvez toujours penser que vous pouvez faire plus. C’est la réalité des logiciels d’écriture: vous aurez toujours le sentiment que quelque chose de plus aurait pu être fait «mieux». Vous devez donc trouver un équilibre entre faire un travail professionnel et plaquer de l’or.
  • Acceptez le fait que le nettoyeur que vous essayez et conservez votre code, le moins gonflé de votre code semblera.

3
+1 Qu'on le veuille ou non, une interface graphique prend en charge un zillion d'opérations détaillées et cela signifie du code.
Patrick Hughes

Les développeurs doivent apprendre à utiliser le codage par événement pour l'interface graphique.
David Gao

23

Je pense que bon nombre des problèmes que vous rencontrez peuvent être imputés à une cause simple. La plupart des développeurs ne traitent pas le code d'interface graphique comme un code «réel». Je n'ai aucune preuve ou statistique ici, juste mon instinct.

Peut-être pensent-ils qu'il ne s'agit que d'une présentation et n'est pas important. " Il n'y a pas de logique commerciale ", disent-ils, " pourquoi l'unité le teste "? Ils rient lorsque vous parlez de l'orientation des objets et de l'écriture de code propre. Ils n'essayent même pas d'améliorer les choses. Il n'y a pas de structure pour commencer, ils tapent simplement du code et le laissent pourrir tandis que d'autres ajoutent leur propre contact au fil du temps. Un beau bordel, un code graffiti.

Le code d'interface graphique a ses propres défis, il doit donc être traité différemment et avec respect. Il a besoin d'amour et de développeurs qui veulent l' écrire. Ceux qui vont le garder mince et lui donner une bonne structure et des modèles corrects.


2
+1 pour faire allusion à la perception d'un code d'interface graphique traité différemment d'un code non-gui. J'ai perdu le compte du nombre de fois où j'ai entendu quelqu'un dire: "Ne vous fatiguez pas à tester l'interface graphique, car elle n'est pas rentable et, en plus, c'est tellement difficile à faire". Je traduis habituellement en "C'est difficile et je suis trop paresseux pour apprendre à le faire!".
S.Robins

1
+1 Là où je travaille, souvent nous ne passons pas en revue le code de l'interface graphique - "c'est juste une interface graphique, ignorez-le". Et je suis aussi coupable que quiconque. Chose étrange, dans mes projets personnels, je passe beaucoup de temps à essayer d’obtenir du code propre et agréable. Je suppose que c'est juste une affaire de culture.
HappyCat

8

Pour une raison quelconque, le code d'interface graphique crée un angle mort chez les développeurs concernant la séparation des préoccupations. C'est peut-être parce que tous les tutoriels regroupent tout dans une classe. C'est peut-être parce que la représentation physique fait en sorte que les choses semblent plus étroitement couplées qu'elles ne le sont. Peut-être est-ce dû au fait que les classes se construisent lentement afin que les gens ne se rendent pas compte qu'ils ont besoin d'une refactorisation, comme si on faisait bouillir la grenouille proverbiale en augmentant lentement la chaleur.

Quelle que soit la raison, la solution consiste à rendre vos cours beaucoup plus petits. Je le fais en me demandant continuellement s'il est possible de mettre ce que je tape dans une classe séparée. S'il est possible de mettre dans une autre classe, et je peux penser à un nom simple et raisonnable pour cette classe, alors je le fais.


6

Vous voudrez peut-être jeter un coup d'œil au motif Présentation de vue modèle / Vue passive. Ray Ryan a donné une bonne conférence sur un IO Google concernant les meilleures pratiques d’architecture pour GWT.

http://www.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html

Il est facile d’abstraire les idées dans d’autres cadres et langues. Le principal avantage de MVP (à mon avis) est la testabilité unitaire. Et vous n’obtenez que cela, si votre code n’est pas gonflé et pas spaghetti (à en juger par votre question, c’est ce que vous voulez). Cela fonctionne en introduisant une couche logique de vue appelée le présentateur. La vue réelle en est découplée via une interface (et peut donc être facilement simulée lors de tests unitaires). Maintenant, puisque votre couche logique de vue (le présentateur) est libérée des éléments internes de la structure d'interface graphique concrète, vous pouvez l'organiser comme du code standard et vous n'êtes pas lié, par exemple, à la hiérarchie d'héritage Swings. Idéalement, vous pouvez basculer les implémentations d'interface graphique dans différents frameworks, à condition qu'ils soient conformes à la même interface.


1
+1 MVP se concentre précisément sur la façon d'extraire la logique de l'interface graphique graphique dans des classes séparées, ce qui est souvent très différent de ce que les gens comprennent lorsqu'ils parlent de MVC.
Doc Brown

5

Ma réponse comprend quatre parties: structure, simplicité, tests et syntaxe.

Les trois premiers sont vraiment difficiles à faire!

Structure signifie accorder une attention particulière à l'utilisation du moins de code possible et du maximum de frameworks, de bibliothèques, etc.

La simplicité signifie garder les choses simples, de la conception initiale à la mise en œuvre réelle. Garder la navigation simple, utiliser des plugins simples, garder la mise en page assez "simple" aidera tous ici. Elles peuvent maintenant être «vendues» à des clients / utilisateurs qui peuvent rapidement voir les avantages des pages fonctionnant sur des ordinateurs, des ipad, des appareils mobiles et autres.

Les moyens de test incluent des outils de test de navigateur (webrat et capybara me viennent à l’esprit avec mon travail sur les rails) qui détectent les problèmes inter-navigateurs dès le début, alors qu’il est possible de concevoir un meilleur code pour les traiter au début, par opposition aux «correctifs» fréquents du code. par différents développeurs car ils sont "découverts" par les utilisateurs de différents navigateurs.

Syntaxe. Il est vraiment utile d’utiliser un vérificateur de code / IDE / éditeur-plugin, etc. pour vos langages HTML, CSS, Javascript, etc. Un outil qui vérifie votre format HTML est donc essentiel. Avoir du HTML bien formé est très utile pour avoir du HTML sans fioritures car un mauvais code devrait avoir plus de visibilité.


4

La solution que j'ai trouvée est un code déclaratif. Utiliser uniquement du code de procédure est une recette pour le code d'interface graphique spaghetti. Bien sûr, une "manière spéciale de peindre le widget" restera probablement du code. Mais c'est du code isolé dans une classe. Gestionnaires d'événements, raccourcis clavier, tailles de fenêtres - tout ce qui est en désordre est mieux déclaré.


4

Il y a beaucoup de bonnes réponses ici.

Une chose qui m'a aidé à simplifier le code de l'interface graphique est de m'assurer que celle-ci dispose de son propre modèle de données.

Pour prendre un exemple simple, si j’ai une interface graphique avec 4 champs de saisie de texte, j’ai une classe de données distincte qui conserve le contenu de ces 4 champs de saisie de texte. Les interfaces graphiques plus complexes nécessitent davantage de classes de données.

Je conçois une interface graphique comme un modèle - vue. Le modèle d'interface graphique est contrôlé par le contrôleur d'application du modèle d'application - view - controller. La vue de l'application correspond au modèle d'interface graphique plutôt qu'au code de l'interface graphique elle-même.


2

Les applications telles que le traitement de texte, les éditeurs graphiques, etc. ont des interfaces complexes et leur code ne peut être simple. Cependant, pour les applications métier, l'interface graphique n'a pas besoin d'être aussi complexe, mais elle l'est toujours.

Certaines des clés de la simplification de l'interface graphique sont (la plupart s'appliquent à .NET):

  1. Efforcez-vous de conception plus simple chaque fois que possible. Évitez les comportements fantaisistes s'ils ne sont pas demandés par l'entreprise.

  2. Utilisez un bon fournisseur de contrôles.

  3. Ne créez pas de fonctionnalité de contrôle personnalisé dans le code client même. Créez plutôt des contrôles utilisateur qui étendent le contrôle d'origine de sorte que vous puissiez refléter vos comportements spécifiques dans les contrôles plutôt que dans le code du formulaire / de la page utilisant.

  4. Utilisez un cadre (même développé localement) pour gérer l’internationalisation, la gestion des ressources, les styles, etc. afin de ne pas répéter ce code dans chaque interface utilisateur.

  5. Employer un composant (ou un framework) pour la navigation.

  6. Construire des dialogues standard pour les erreurs, les avertissements, les confirmations, etc.


1

Appliquez la conception orientée objet à votre code et pour le développement d'interface utilisateur:

  1. Présentation séparée et modèle Utilisez une bibliothèque / structure MV-quelconque, ou écrivez la vôtre, pour séparer la logique de vue / contrôleur du modèle de données. Toutes les communications avec le backend doivent être effectuées à l'intérieur du modèle et l'état du modèle doit toujours être synchronisé avec le backend.
  2. Découplage Si l'objet A connaît l'objet B, alors A peut appeler des méthodes sur B, mais B ne doit pas connaître A, mais A peut écouter les événements de B. Il s'assure qu'il n'y a pas de dépendance circulaire. Si votre application contient de nombreux événements entre les composants, créez un EventBus ou utilisez un cadre basé sur des événements tel que Twitter Flight.
  3. Rendu partiel ou intégral Si votre vue est un tableau ou une liste d'éléments, vous pouvez être tenté de créer des méthodes telles que "ajouter", "supprimer" pour insérer / supprimer un élément dans / de la collection. Votre code pourrait facilement gonfler lorsque vous devez prendre en charge le tri et la pagination. Mon conseil est donc le suivant: restituez simplement l’ensemble de la vue, même en cas de modification partielle. Qu'en est-il de la performance? Eh bien, si votre collection est grande, alors vous devriez faire la pagination de toute façon. Développeur Web: assurez-vous que vos gestionnaires d'événements sont délégués à l'élément racine de la vue, qui ne change pas.
  4. Modèle de vue Lorsque l'état de votre vue devient trop compliqué à gérer, par exemple, une vue Table doit garder une trace des données de ligne, des données de colonne, de l'ordre de tri, des lignes actuellement cochées (si elle prend en charge le contrôle multiple), etc., vous devriez probablement créer un objet ViewModel pour ces états. Votre objet View doit appeler des paramètres sur le ViewModel si quelque chose change sur l'interface utilisateur (par exemple: l'utilisateur vérifie une ligne); et il devrait répondre à l'événement de modification de ViewModel en mettant à jour l'interface utilisateur. En règle générale, évitez de mettre à jour l'interface utilisateur si l'événement change est déclenché par l'interface utilisateur.

Voici une application petite mais non-triviale pour aider à illustrer certains de mes points. Vous pouvez trouver le diagramme d'interaction code et vue / modèle ici: https://github.com/vanfrankie/pushpopbox


0

Vous voulez jeter un oeil sur le concept de "liaison de données" . C'est un moyen de connecter les éléments de l'interface utilisateur aux éléments de modèle abstraits de manière déclarative, de sorte que les éléments de modèle soient automatiquement synchronisés avec le contenu de l'interface utilisateur. Cette approche présente de nombreux avantages, par exemple le fait de ne pas avoir à écrire les gestionnaires d’événements vous-même pour synchroniser les données.

Il existe un support de liaison de données pour de nombreux frameworks d'interface utilisateur, par exemple .NET et Eclipse / JFace .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.