Refaktoryzacja dużej klasy

WB
  • Rejestracja:prawie 4 lata
  • Ostatnio:10 miesięcy
  • Postów:18
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ę?

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 17 godzin
  • Lokalizacja:Wrocław
3

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

WB
  • Rejestracja:prawie 4 lata
  • Ostatnio:10 miesięcy
  • Postów:18
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.

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:około 17 godzin
  • Lokalizacja:Wrocław
0

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

Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około godziny
  • Lokalizacja:Laska, z Polski
  • Postów:10056
2

Testy jakiekolwiek pod to jakieś są?

somekind
Dobre pytanie, testy spaghetti na wątkach musiałyby cudnie wyglądać. :D
Riddle
@somekind: Nic mnie już nie zdziwi. Raz widziałem jak ktoś użył PowerMocka żeby podmienić Thread.currentThread(). (to taka libka która umie podmienić implementacje metod statycznych).
somekind
I to działało? :|
Riddle
Testy się odpalały; ale były kruche jak 150. Tzn żę dowolna zmiana w kodzie powodowało 100 faili w kodzie, i trzebabyło taśmą testy sklejać; ale nie po to żeby jeszcze coś testowały, tylko po to żeby je wyciszyć. Na szczęście krótko byłem w tym projekcie.
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około godziny
  • Lokalizacja:Laska, z Polski
  • Postów:10056
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ą.

edytowany 1x, ostatnio: Riddle
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)