Czy umieszczenie pracownika wątku / tła w klasie jest „niewłaściwe” / zły projekt?

15

Mam klasę, która będzie czytać z Excela (C # i .Net 4), aw tej klasie mam proces roboczy w tle, który załaduje dane z Excela, podczas gdy interfejs użytkownika może pozostać responsywny. Moje pytanie brzmi: czy źle jest mieć pracownika pracującego w tle w klasie? Czy powinienem utworzyć swoją klasę bez niej i użyć pracownika działającego w tle do działania na tej klasie? Nie widzę żadnych problemów z tworzeniem mojej klasy w ten sposób, ale z drugiej strony jestem nowicjuszem, więc pomyślałem, że upewnię się, zanim będę kontynuować.

Mam nadzieję, że to pytanie jest istotne tutaj, ponieważ nie sądzę, że powinno to dotyczyć przepływu stosu, ponieważ mój kod działa, to tylko kwestia projektu.

Jetti
źródło
3
jak myślisz, dlaczego może być źle?
Alb
1
@Alb - trudno powiedzieć. Mój kod działa i spełnia moje potrzeby, jednak planuję użyć tego w projekcie, który stworzę jako open source. Chcę się upewnić, że mój kod „nie działa” i jest właściwie dobrze zaprojektowany.
Jetti

Odpowiedzi:

21

Czy powinienem utworzyć swoją klasę bez niej i użyć pracownika działającego w tle do działania na tej klasie?

Tak, powinieneś. I powiem ci, dlaczego - naruszasz zasadę pojedynczej odpowiedzialności . Poprzez ścisłe powiązanie klasy, która uzyskuje dostęp do dokumentu programu Excel, z tym, w jaki sposób uzyskuje on dostęp do dokumentu programu Excel, eliminuje się zdolność kodu „kontrolera” (dowolnego kodu, który tego używa), aby zrobić to w inny sposób. Jak inaczej możesz zapytać? Co jeśli kod kontrolera ma dwie operacje, które zajmują dużo czasu, ale chcą, aby były sekwencyjne? Jeśli zezwoliłeś kontrolerowi na obsługę wątków, może on wykonywać oba długoterminowe zadania razem w jednym wątku. Co zrobić, jeśli chcesz uzyskać dostęp do dokumentu programu Excel z kontekstu innego niż interfejs użytkownika i nie potrzebujesz wątku?

Przenosząc odpowiedzialność za wysyłanie wątków na osobę wywołującą, zapewniasz większą elastyczność kodu, dzięki czemu jest on łatwiejszy do ponownego wykorzystania.

Nemi
źródło
2
+1 za udzielenie odpowiedzi na zadane pytanie i dobrą odpowiedź.
Adam Lear
+1 - Dziękuję Nemi. Odpowiedziałeś na moje pytanie, doceniam to. Wyciągnę to z mojej klasy i umieszczę w nowej klasie, jeszcze raz dziękuję!
Jetti
@Nemi - więc czy byłoby do przyjęcia, gdybym stworzył inną metodę, która byłaby ładowaniem synchronicznym, a następnie miałaby metodę asynchroniczną? A może po prostu lepiej byłoby zastosować jedną metodę ładowania synchronicznego, a następnie przejść od tego momentu?
Jetti
1
Ja osobiście nie miałbym metody synchronizacji i metody asynchronicznej. Nadal łączysz obowiązki. Wiele razy pracowałem z tym właśnie problemem. To, co zrobiłem, stworzyło TaskController, który był wzorowany na SwingWorker, ale miał określony kod dla naszej aplikacji. TaskController robił takie rzeczy, jak aktualizacja paska postępu, zmiana kursora myszy itp. Może wydawać się, że kod płyty kotłowej musi zawsze tworzyć TaskController do wykonywania połączeń, ale ostatecznie kod jest bardziej niezawodny i łatwiejszy w utrzymaniu.
Nemi
Dzięki Nemi! Chciałbym móc jeszcze raz cię głosować, ponieważ zdecydowanie odpowiedziałeś na moje pytanie i wskazałeś mi właściwy kierunek. Jeszcze raz dziękuję!!
Jetti
3

Dobrze jest projektować operacje interfejsu użytkownika w oddzielnym wątku od zadań w tle. W przeciwnym razie interfejs użytkownika przestanie odpowiadać, gdy aplikacja będzie zajęta.

Jeśli możesz oddzielić część działającą w wątku tła od własnej klasy, kod będzie czystszy.

Alba
źródło
1
Choć słusznie, po przeczytaniu jego pytania nie ma problemu ze zrozumieniem tego punktu.
Nemi
Przepraszam, jeśli moje pytanie zostało źle odczytane. Wiem, że oddzielenie interfejsu użytkownika i zadań w tle jest dobrym projektem (jeśli nie jest to konieczne, mój testowy plik Excel ma ponad 8k wierszy i spowodowałby, że program nie odpowiadał). Moje pytanie jest w zasadzie, czy dobrze jest ściśle połączyć wątek z klasą Excel.
Jetti
0

Oddzieliłbym twój interfejs od zadania w tle, używając oddzielnych klas. Takie postępowanie zachęca do rozdzielenia obaw. Kod interfejsu użytkownika i logika biznesowa nie powinny być mieszane.

Ryan Michela
źródło
Dla jasności moja klasa nie dotyka interfejsu użytkownika. Ma dwa zdarzenia, które pozwolą cokolwiek go używa do aktualizacji interfejsu użytkownika, jeśli to konieczne, ale moja klasa sama w sobie nie dotyka interfejsu użytkownika.
Jetti
0

Z tego, co pamiętam w BackgroundWorkers, jest to, że zapewniają one szereg metod konwergencji, takich jak możliwość wysyłania aktualizacji postępu do interfejsu użytkownika. Nie ma jednak reguły, która mówi, że nie możesz jej używać z innej klasy.

Również jeśli wykonujesz iterację, która nie wymaga przetwarzania elementów w określonej kolejności, rozważ użycie zamiast tego ThreadPool (lub jeśli korzystasz z .NET 4, użyj biblioteki zadań równoległych ).

Michael Brown
źródło
Dzięki za twoją odpowiedź. W mojej klasie utworzyłem dwa zdarzenia, które będą wyzwalane podczas postępu (w zasadzie zawijanie zdarzenia postępu BackgroundWorker, ponieważ BackgroundWorker jest prywatny), który wysyła postęp do interfejsu użytkownika (pasek postępu).
Jetti