web-dev-qa-db-fra.com

Découpler le code de l'interface utilisateur?

Dans ma candidature, j'ai plusieurs manipulateurs d'événements qui effectuent une action en réponse aux événements d'interface utilisateur, tels qu'un clic bouton ou une sélection de menu. Le code dans ces gestionnaires d'événements ressemble à ceci par exemple:

void MyDialog::OnCommandLoadFile()
{
    char strFilter[] = { "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||" };

    CFileDialog fileDlg(TRUE, ".txt", NULL, 0, strFilter);

    if( fileDlg.DoModal() == IDOK )
    {
        const std::string path = fileDlg.GetPathName();

        std::vector<std::string> urls;
        int maxNumberOfUrls = settings_->Get<int>(Settings::MaxNumberOfUrls);
        UrlProvider *urlProvider = new FileUrlProvider(path);
        urlProvider->GetUrls(urls, maxNumberOfUrls);

        webMonitoringService_->MonitorUrls(urls);

        DisplayMessage("URLs loaded.");
    }

}

Ceci est typique de mes manutentionnaires d'événements. Je ne codé pas les gestionnaires d'événements monolithiques, mais ils "collent" différentes pièces ensemble. J'ai généralement des classes de logique/service professionnel que l'interface utilisateur a une référence à et dans les gestionnaires d'événements, je fais des appels à un ou plusieurs d'entre eux pour effectuer la tâche souhaitée.

Ce que je me demande, c'est que cela reste trop couplé? Si je voulais transformer cela en une application de ligne de commande, je devrais extraire des bits et des pièces du code de l'interface utilisateur. Dans l'exemple ci-dessus, il y a une action composée de sorte que je pense que cela devrait ressembler davantage à:

void MyDialog::OnCommandLoadFile()
{
    char strFilter[] = { "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||" };

    CFileDialog fileDlg(TRUE, ".txt", NULL, 0, strFilter);

    if( fileDlg.DoModal() == IDOK )
    {
        const std::string path = fileDlg.GetPathName();

        application->MonitorUrlsFromFile(path);

        DisplayMessage("URLs loaded.");
    }

}

Est-ce correct? Dans le code ci-dessus, quelle serait la "application"? Un contrôleur? Est-ce la bonne abstraction pour cela? Chaque fois que je dois effectuer une tâche dans un gestionnaire d'interface utilisateur, il s'agissait toujours d'une seule doublure (en dehors d'obtenir des données dans ou des zones de texte, etc.) Quelqu'un a un pointeur sur un didacticiel qui couvre vraiment cela (dans n'importe quel langage de programmation. )?

De plus, si vous devriez abstraiter des actions composées, comment propager des messages d'erreur de l'abstraction composée à l'interface utilisateur? Une hiérarchie d'exception? Une exception unique avec le code d'erreur?

[~ # ~ ~] Mise à jour [~ # ~]

Je regarde le modèle de commande qui semble prometteur.

Mise à jour 2

À la fin, j'ai décidé de refactoriser mon code comme décrit dans le document de la boîte de dialogue humble dans la réponse acceptée. Ma compréhension est que c'est un modèle de type de présentateur de modèle (MVP) et plus particulièrement une vue passive. Le refactoring se passe bien. Il existe des arêtes brutes dans ma compréhension de l'application complète du modèle lorsqu'il s'agit d'afficher des boîtes de message et de déposer des boîtes de dialogue Ouvrir ou de traiter avec les minuteries d'événements, mais dans l'ensemble, je suis plus heureux avec mon code; Le couplage serré me dérangeait.

J'ai également trouvé ce "paquet": Modèles et pratiques Web Developer Developer Developer: Bundle Afficher le modèle (MVP) qui a un échantillon de code MVP. C'est un projet Web, tandis que ma mise en œuvre actuelle est de bureau mais je suppose que la beauté du MVP est que cela n'a pas d'importance. Une chose que j'ai eue de c'était à ce que la dépendance injecte mon élément de la couche de service dans le présentateur plutôt que de les nouveaux dans mon cours de présentateur.

7
User

Votre refactoring est bien, surtout si MonitorUrlsFromFile() est dupliqué de code. Vous n'avez pas besoin de "découplage de l'interface utilisateur" pour justifier que le refactoring; Cela peut être justifié sur ses propres mérites.

Le schéma de propagation d'erreur dépend d'un certain nombre de facteurs. Généralement, j'utilise un objet BackgroundWorker objet ou un thread ou quelque chose de similaire pour recevoir des événements d'état d'une tâche d'arrière-plan longue en cours, mais le projet que je travaille actuellement sur plusieurs validations de règles d'entreprise apparaîtra sur l'interface utilisateur. Mon approche va changer là-bas.

Le moyen le plus simple de gérer la propagation d'erreur de base est de fournir un gestionnaire d'exception de niveau supérieur dans votre méthode Main() _ Méthode ou tout ce qui appelle votre boucle de traitement.

5
Robert Harvey