Refaktoryzacja dużej klasy

0

Zacząłem refaktoryzować aplikację napisaną w C# (WinForms) i zastanawiałem się jak rozsądnie podzielić prace i sam kod.
Niestety nie mam doświadczenia jak podejść do tego tematu i wydaje się dość trudny.

Obecnie cała logika a dodatkowo tworzenie/zarządzanie wątkami jest w jednej klasie a dokładniej MainForm.cs.

Czyli w jednym pliku mamy:

  • około 9 tys. linii kodu
  • wątki, które mają współdzielone zasoby bez żadnych locków
  • locki przy zapytaniach do bazy danych, bo współdzielone jest połączenie do bazy danych
  • locki przy komunikacji z urządzeniami, które są zbędne

Architektura wygląda tak, że mamy (od dołu):

  • modele z bazy danych
  • coś w stylu UberGigaDataProvider.cs, który zapisuje i odczytuje modele z bazy danych
  • coś w stylu UberGigaService.cs, które robi wszystko co chcemy przy użyciu modeli i UberGigaDataProvider.cs
  • MainForm.cs, która używa UberGigaService.cs, UberGigaDataProvider.cs i swojej logiki do wykonania określonego zadania

Co robi FormMain.cs:

  • wyświetla dane i statystyki z urządzeń
  • tworzy wątek, który pisze do bazy, że aplikacja działa
  • tworzy wątek, który obsługuje zdarzenia z bazy danych np. programuj urządzenia, przerwij odczyt z urządzeń, wykonaj akcje x na urządzeniach
  • tworzy np. 10 wątków, które np. odczytują dane z urządzeń, zapisują do bazy danych i programują urządzenia

Obecnie co zrobiłem:

  • wyciągnąłem metody, które były niezwiązane z MainForm.cs do osobnych klas - pozwoliło to zredukować plik do około ~5 tys. linii kodu
  • usunąłem locki - ma to na celu, aby każdy wątek miał swoją instancję UberGigaDataProvider.cs i UberGigaService.cs

Dalej chciałbym:

  • stworzyć prostą klasę abstrakcyjną np. BaseThreadManager (albo sam interfejs, który będzie punktem startowym wątku i zarządzać nim z innego poziomu), która będzie hermetyzować proces tworzenia, startu i zatrzymania wątku z abstrakcyjną metodą ThreadFn (ta metoda to po prostu funkcja startu wątku)
  • dla każdego typu wątku stworzyć nową klasę np. LifeThreadManager, która będzie dziedziczyć po BaseThreadManager i tam będzie cały proces obsługi pisania do bazy danych
  • dalej np. ReadThreadManager, która będzie cały czas odczytywać dane z urządzeń (chociaż ta klasa będzie musiała być inna, bo będzie zarządzać pulą wątków, a nie tylko jednym wątkiem)
  • dalej np. EventThreadManager która będzie dziedziczyć po BaseThreadManager i będzie tam cały proces obsługi zdarzeń z bazy danych

Zastanawiam się, w jaki sposób zrobić obsługę tych zdarzeń z EventThreadManager.
Myślałem żeby zrobić dodatkową fasadę, która będzie reagowała na zdarzenia z EventThreadManager i zarządzała klasami ReadThreadManager, LifeThreadManager a dodatkowo wypuszczała zdarzenia do kontrolek np. poprzez eventy.

Celem jest ogólna poprawa programu.
Chcę to zrobić prosto i spróbować uniknąć over-engineeringu.

Pytanie, czy idę w dobrą stronę?

3

@wielki_bebzon: najlepszym rozwiązaniem byłoby pozbycie się Thread i ręcznego zarządzania nimi. Po co Ci to?

0

@somekind: masz na myśli async? Niestety to by wymagało przepisania projektu na C# bo trochę skłamałem z tym językiem (nie chciałem zbyt dużo informacji podawać).

Pomogłoby to z komunikacją i blokowaniem I/O, ale nie wiem jakby to miało rozwiązać problem kilku wątków, które robią różne zadania.

0

Mam na myśli dowolny sposób przetwarzania operacji w tle: Task, BackgroundWorker, itd.
Ale jak piszesz w Javie, to nie pomogę.

2

Testy jakiekolwiek pod to jakieś są?

1

@wielki_bebzon: Ja na Twoim miejscu raczej bym nie refaktorowałbym tej klasy.

Ja podszedłbym do tego tak, że zastanowiłbym się, czy jest tam kawałek logiki albo kawałek UI'a, ewentualnie kawałek kodu który strzela do bazy i możnaby go jakoś sensownie wynieść.z tej klasy. Mówiąc "Sensownie" mam tu na myśli, tak żeby nie zachować żadnego powiązania z tą starą klasą i jej składnikami. Jak już wyniesiesz pierwszą taką funkcję lub obiekt, to możesz jej użyć w tej ugly klasie, tak żeby aplikacja działała. I potwarzać.

Do wydzielonych fragmentów dopisałbym testy; i spokojnie, iteracyjnie, całą logika wychodziłaby sobie z tej klasy, a klasa sama w sobie by malala coraz bardziej, aż w końcu cała logika znalazłaby się poza nią, i możnaby ją wtedy wywalić.

Pomysły z wielkim refaktorem się raczej nie udają; a z wielkim przepisaniem są kosztowne, i też czasme się nie udają.

1 użytkowników online, w tym zalogowanych: 0, gości: 1