Est-ce «mauvais» / mauvais design de mettre un thread / travailleur en arrière-plan dans une classe?

15

J'ai une classe qui lira à partir d'Excel (C # et .Net 4) et dans cette classe, j'ai un travailleur en arrière-plan qui chargera les données d'Excel tandis que l'interface utilisateur peut rester réactive. Ma question est la suivante: est-ce une mauvaise conception d'avoir un travailleur en arrière-plan dans une classe? Dois-je créer ma classe sans elle et utiliser un travailleur en arrière-plan pour opérer sur cette classe? Je ne vois vraiment aucun problème de créer ma classe de cette façon, mais là encore, je suis un débutant, alors j'ai pensé que je m'assurerais avant de continuer.

J'espère que cette question est pertinente ici car je ne pense pas qu'elle devrait être sur stackoverflow pendant que mon code fonctionne, c'est juste un problème de conception.

Jetti
la source
3
pourquoi pensez-vous que cela pourrait être faux?
Alb
1
@Alb - c'est difficile à dire. Mon code fonctionne et répond à mes besoins, cependant, je prévois de l'utiliser dans un projet que je ferai open source. Je veux m'assurer que mon code ne "fonctionne pas seulement" et est bien conçu.
Jetti

Réponses:

21

Dois-je créer ma classe sans elle et utiliser un travailleur en arrière-plan pour opérer sur cette classe?

Oui tu devrais. Et je vais vous dire pourquoi - vous violez le principe de responsabilité unique . En couplant étroitement la classe qui accède à la doc Excel avec la façon dont elle accède à la doc Excel, vous éliminez la possibilité pour le code "contrôleur" (tout code qui l'utilise) de le faire d'une manière différente. Comment différent, vous pouvez demander? Que se passe-t-il si le code du contrôleur comporte deux opérations qui prennent beaucoup de temps mais souhaitent qu'elles soient séquentielles? Si vous avez autorisé le contrôleur à gérer le threading, il peut effectuer les deux tâches de longue durée ensemble dans un seul thread. Que se passe-t-il si vous souhaitez accéder au document Excel à partir d'un contexte non UI et que vous n'avez pas besoin qu'il soit enfilé?

En transférant la responsabilité du filetage à l'appelant, vous permettez une plus grande flexibilité de votre code, le rendant plus réutilisable.

Nemi
la source
2
+1 pour avoir répondu à la question posée et pour y avoir bien répondu.
Adam Lear
+1 - Merci Nemi. Vous avez répondu à ma question sur place, je l'apprécie. Je vais retirer cela de ma classe et le mettre dans une nouvelle classe, merci encore!
Jetti
@Nemi - serait-il donc acceptable si je créais une autre méthode qui serait une charge synchrone et aurait ensuite une méthode asynchrone? Ou serait-il préférable d'avoir la seule méthode de chargement synchrone, puis de partir de là?
Jetti
1
Personnellement, je n'aurais pas de méthode de synchronisation ni de méthode asynchrone. Vous associez toujours des responsabilités. J'ai travaillé avec ce problème exact plusieurs fois. Ce que j'ai fait a été créé un TaskController qui a été modelé d'après SwingWorker, mais qui avait un code spécifique pour notre application. Le TaskController a fait des choses comme mettre à jour une barre de progression, changer le curseur de la souris, etc. Il peut sembler que le code de la plaque de la chaudière doive toujours créer un TaskController pour passer des appels, mais finalement le code est plus robuste et plus maintenable.
Nemi
Merci Nemi! J'aimerais pouvoir vous voter à nouveau car vous avez définitivement répondu à ma question et m'avez pointé dans la bonne direction. Merci encore!!
Jetti
3

C'est une bonne conception que les opérations d'interface utilisateur fonctionnent dans un thread séparé des tâches d'arrière-plan. Sinon, l'interface utilisateur ne répond plus lorsque l'application est occupée.

Si vous pouvez séparer la partie qui fonctionne dans le thread d'arrière-plan de sa propre classe, le code sera plus propre.

Alb
la source
1
Bien que correct, d'après la façon dont j'ai lu sa question, il n'a aucun problème à comprendre ce point.
Nemi
Je suis désolé si ma question a été mal lue. Je sais que séparer l'interface utilisateur et les tâches d'arrière-plan est une bonne conception (si ce n'est pas nécessaire, mon fichier Excel de test est sur 8k lignes et ferait que le programme ne répondrait pas). Ma question est essentiellement de savoir s'il est bon de coupler étroitement le fil avec la classe Excel.
Jetti
0

Je séparerais votre interface utilisateur de votre tâche d'arrière-plan en utilisant des classes distinctes. Cela encourage la séparation des préoccupations. Le code d'interface utilisateur et la logique métier ne doivent pas être mélangés.

Ryan Michela
la source
Juste pour être clair, ma classe ne touche pas à l'interface utilisateur. Il a deux événements qui permettraient à tout ce qui l'utilise de mettre à jour l'interface utilisateur si nécessaire, mais ma classe en elle-même ne touche pas l'interface utilisateur.
Jetti
0

D'après ce que je me souviens à propos de BackgroundWorkers, ils fournissent un certain nombre de méthodes pratiques comme la possibilité d'envoyer des mises à jour de progression à l'interface utilisateur. Il n'y a pas de règle qui dit que vous ne pouvez pas l'utiliser à partir d'une classe différente.

De plus, si vous effectuez une itération qui ne nécessite pas le traitement des éléments dans un ordre spécifique, envisagez d'utiliser le ThreadPool à la place (ou si vous êtes sur .NET 4, utilisez la bibliothèque parallèle de tâches ).

Michael Brown
la source
Merci pour votre réponse. J'ai créé deux événements dans ma classe qui déclencheront la progression (encapsulant essentiellement l'événement de progression BackgroundWorker, car BackgroundWorker est privé) qui envoie la progression à l'interface utilisateur (barre de progression).
Jetti