Jak najlepiej unikać pisania rozdętego kodu GUI?

48

Za każdym razem, gdy pracuję z kodem GUI, kod puchnie szybciej niż inne rodzaje kodu. Refaktoryzacja wydaje się również trudniejsza. Podczas gdy w innych rodzajach kodu mogę dość łatwo refaktoryzować - odkrywam, że mogę rozłożyć większą klasę na mniejsze części funkcjonalności - z większością struktur GUI często jestem związany ze strukturą, która wymaga mojego widget / control / jakiejkolwiek klasy zaimplementuj o wiele więcej rzeczy bardziej bezpośrednio w widżecie / kontroli / czymkolwiek. Czasami wynika to z konieczności (a) odziedziczenia niektórych podstawowych widgetów / kontroli / rzeczy lub (b) potrzeby dostępu do chronionych metod.

Zazwyczaj muszę także na przykład odpowiadać na dużą różnorodność sygnałów wejściowych za pośrednictwem sygnałów / zdarzeń / cokolwiek z frameworka, aby wdrożyć wszystkie tryby interakcji z użytkownikiem. Mogę potrzebować jednego widgetu / kontrolki GUI do obsługi wielu różnych danych wejściowych / wyjściowych, które mogą obejmować:

  1. menu prawym przyciskiem myszy / menu kontekstowe
  2. reagowanie na wybory z menu kontekstowego - których może być wiele
  3. specjalny sposób na malowanie GUI
  4. reagować na wprowadzanie danych z klawiatury
  5. przyciski, pola wyboru,
  6. itd itd

... cały czas zarządzaj klasami w GUI reprezentującymi logikę biznesową.

Prosty, prosty GUI może sprawić, że jego kod rozrośnie się dość szybko, nawet przy oddzielnej logice biznesowej i przy użyciu MVC, uważam, że kod GUI jest dużym magnesem do zmian.

Czy istnieje jakiś sposób na zarządzanie kodem GUI w rozsądny sposób i unikanie zepsucia okna? A może masa programów do obsługi zdarzeń losowych / przesłoniętych metod jest naprawdę najlepsza dla kodu GUI?

Doug T.
źródło
4
Jaka jest twoja dokładna definicja „wzdęcia”?

Odpowiedzi:

36

Należy pamiętać o kodzie GUI, ponieważ jest on sterowany zdarzeniami, a kod sterowany zdarzeniami zawsze będzie wyglądał jak masa losowo zorganizowanych procedur obsługi zdarzeń. To, co robi się naprawdę nieuporządkowane, jest wtedy, gdy próbujesz wprowadzić do klasy kod, który nie jest sterowany zdarzeniami. Jasne, wygląda na to, że zapewnia obsługę programów obsługi zdarzeń i możesz sprawić, by procedury obsługi zdarzeń były miłe i małe, ale cały ten dodatkowy kod pomocniczy unoszący się wokół sprawia, że ​​twoje źródło GUI wydaje się nadęte i niechlujne.

Co więc możesz na to poradzić i jak ułatwić refaktoryzację? Cóż, najpierw zmieniłbym definicję refaktoryzacji z czegoś, co robię czasami, na coś, co robię ciągle podczas kodowania. Dlaczego? Ponieważ chcesz, aby refaktoryzacja umożliwiła łatwiejszą modyfikację kodu, a nie na odwrót. Nie proszę cię po prostu o zmianę semantyki tutaj, ale zamiast tego proszę o odrobinę mentalnej kalisteniki, aby inaczej zobaczyć kod.

Trzy najczęściej stosowane przeze mnie techniki refaktoryzacji to Rename , Extract Method i Extract Class . Gdybym nigdy nie nauczył się żadnego innego refaktoryzacji, te trzy nadal pozwoliłyby mi utrzymać kod w czystości i dobrze ustrukturyzowany, a z treści twojego pytania brzmi, jakbyś prawdopodobnie używałby tych samych trzech refaktoryzacji prawie stale w aby Twój kod GUI był cienki i czysty.

Możesz mieć najlepszą możliwą separację GUI i logiki biznesowej na świecie, a mimo to kod GUI może wyglądać, jakby kopalnia kodu została zdetonowana w środku. Moja rada jest taka, że ​​nie zaszkodzi mieć dodatkową klasę lub dwie, które pomogą ci właściwie zarządzać GUI, i niekoniecznie muszą to być Twoje klasy View , jeśli stosujesz wzorzec MVC - chociaż często znajdziesz klasy pośredniczące są tak podobne do twojego poglądu, że często odczuwasz potrzebę połączenia ich dla wygody. Uważam, że dodanie dodatkowej warstwy specyficznej dla GUI do zarządzania całą logiką wizualną nie zaszkodzi, ale prawdopodobnie chcesz rozważyć korzyści i koszty związane z tym.

Dlatego radzę:

  • Nie rób nic bezpośrednio za GUI, oprócz wywoływania i definiowania sposobu, w jaki GUI będzie się łączyć z widokiem (lub warstwą pośrednią).
  • Nie próbuj klaksonować każdej rzeczy związanej z widokiem w jedną klasę - lub nawet jedną klasę na okno GUI - chyba że ma to sens. Alternatywą jest utworzenie wielu małych i łatwych w zarządzaniu klas do zarządzania logiką GUI.
  • Kiedy twoje metody zaczynają wyglądać na nieco większe niż 4-5 linii kodu, sprawdź, czy jest to konieczne i czy możliwe jest wyodrębnienie jednej lub dwóch metod, abyś mógł zachować swoje metody, nawet jeśli oznacza to klasę z wieloma innymi metodami.
  • Jeśli Twoje klasy zaczynają wyglądać na naprawdę duże, zacznij od usunięcia WSZYSTKICH zduplikowanych funkcji, a następnie sprawdź, czy możesz logicznie pogrupować swoje metody, aby wyodrębnić inną klasę lub dwie.
  • Pomyśl o refaktoryzacji za każdym razem, gdy piszesz wiersz kodu. Jeśli pojawi się wiersz kodu do działania, sprawdź, czy możesz go przefaktoryzować, aby uniknąć powielania funkcji, lub uczynić go nieco bardziej uproszczonym bez zmiany zachowania.
  • Zaakceptuj nieuniknione, że zawsze poczujesz, że jedna lub druga część twojego systemu zacznie czuć się trochę wzdęta, szczególnie jeśli zaniedbujesz refaktoryzację w trakcie pracy. Nawet z dobrze faktoringiem kodu bazowego, nadal można poczuć się jak jest więcej, że mógłby zrobić. To jest rzeczywistość pisania oprogramowania, w którym zawsze czujesz, że coś więcej można było zrobić „lepiej”, więc musisz znaleźć równowagę między wykonywaniem profesjonalnej pracy a pozłacaniem.
  • Zaakceptuj, że im czystszy kod spróbujesz zachować, tym mniej rozdęty będzie twój kod.
S.Robins
źródło
3
+1 Podoba ci się czy nie, GUI zajmuje się zillionami szczegółowych operacji, a to oznacza kod.
Patrick Hughes,
Programiści powinni nauczyć się korzystać z kodowania sterowanego zdarzeniami w GUI.
David Gao,
23

Myślę, że wiele napotkanych problemów można przypisać prostej przyczynie. Większość programistów nie traktuje kodu GUI jak „prawdziwego” kodu. Nie mam tutaj żadnych dowodów ani statystyk, tylko moje przeczucie.

Może uważają, że jest to „ tylko prezentacja ” i nieistotne. „ Nie ma tam logiki biznesowej ”, mówią „po co testować to urządzenie ”? Śmieją się, gdy wspominasz o orientacji obiektowej i pisaniu czystego kodu. Nie próbują nawet poprawić sytuacji. Na początku nie ma żadnej struktury, po prostu klepią trochę kodu i pozwalają mu gnić, gdy inni dodają swój własny dotyk w czasie. Piękny bałagan, kod graffiti.

Kod GUI ma swoje wyjątkowe wyzwania, dlatego należy go traktować inaczej i z szacunkiem. Potrzebuje miłości i programistów, którzy chcą to napisać. Te, które utrzymają go cienką i nadadzą mu dobrą strukturę i prawidłowe wzory.

c_maker
źródło
2
+1 za nawiązanie do postrzegania kodu GUI traktowanego inaczej niż kodu nie-GUI. Straciłem rachubę, ile razy słyszałem, jak ktoś powiedział: „nie zawracaj sobie głowy testowaniem GUI, ponieważ nie jest to opłacalne, a poza tym tak trudne”. Zazwyczaj tłumaczę na „To trudne i jestem zbyt leniwy, by nauczyć się, jak to robić!”.
S.Robins,
1
+1 W miejscu pracy często nie sprawdzamy kodu GUI - „to tylko GUI, pomiń”. I jestem tak samo winny jak wszyscy. Dziwne jest to, że w moich osobistych projektach spędzam dużo czasu, próbując uzyskać ładny, czysty kod GUI. Zgadnij, to tylko kwestia kultury.
HappyCat,
8

Z jakiegoś powodu kod GUI powoduje, że programiści nie mają pewności co do rozdzielenia problemów. Może dlatego, że wszystkie samouczki grupują wszystko w jedną klasę. Może dlatego, że fizyczna reprezentacja sprawia, że ​​rzeczy wydają się ściślej powiązane niż są. Może dlatego, że klasy budują się powoli, aby ludzie nie rozpoznali, że potrzebują refaktoryzacji, jak przysłowiowa żaba gotowana przez powolne podkręcanie ognia.

Bez względu na przyczynę rozwiązaniem jest, aby Twoje zajęcia były znacznie mniejsze. Robię to, ciągle zadając sobie pytanie, czy można umieścić to, co wpisuję, w osobnej klasie. Jeśli można umieścić w innej klasie i mogę wymyślić rozsądną i prostą nazwę dla tej klasy, robię to.

Karl Bielefeldt
źródło
6

Możesz rzucić okiem na wzór Prezenter widoków modelu / Widok pasywny. Ray Ryan wygłosił dobry wykład na temat Google IO na temat najlepszych praktyk architektury dla GWT.

http://www.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html

Łatwo jest wyodrębnić pomysły na inne frameworki i języki. Główną zaletą MVP (moim zdaniem) jest testowanie jednostkowe. I dostajesz to tylko, jeśli twój kod nie jest rozdęty i nie jest spaghetti (sądząc po twoim pytaniu, to jest to, czego chcesz). Działa poprzez wprowadzenie warstwy logiki widoku zwanej prezenterem. Rzeczywisty widok jest od tego oddzielony za pomocą interfejsu (a zatem można go łatwo wyśmiewać w testach jednostkowych). Teraz, ponieważ twoja warstwa logiki widoku (prezenter) jest wolna od wewnętrznych elementów konkretnego interfejsu GUI, możesz zorganizować ją jak zwykły kod i nie jesteś związany np. Z hierarchią dziedziczenia Swings. W idealnym przypadku można przełączać implementacje GUI w różnych ramach, o ile są one zgodne z tym samym interfejsem.

szalik
źródło
1
+1. MVP skupia się dokładnie na tym, jak wyodrębnić logikę GUI do oddzielnych klas, co często różni się znacznie od tego, co ludzie rozumieją, gdy mówią o MVC.
Doc Brown
5

Moja odpowiedź składa się z czterech części: struktury, prostoty, testowania i składni.

Pierwsze trzy są naprawdę trudne!

Struktura oznacza zwracanie dużej uwagi na użycie najmniejszej ilości kodu i maksymalnej liczby ram, bibliotek itp.

Prostota oznacza utrzymanie prostoty od początkowego projektu do faktycznej implementacji. Pomoże w tym prosta nawigacja, proste wtyczki, przejrzysty układ. Można je teraz „sprzedać” klientom / użytkownikom, którzy mogą szybko zobaczyć zalety stron, które działają na komputerach PC, iPadach, urządzeniach mobilnych i innych urządzeniach.

Testowanie oznacza w tym narzędzia do testowania przeglądarki (webrat i kapibara przychodzą mi do głowy z działaniem szyn), które wychwytują problemy z różnymi przeglądarkami z góry, gdy można zaprojektować lepszy kod, aby poradzić sobie z nimi na początku, w przeciwieństwie do częstego „łatania” kodu przez różnych programistów, ponieważ są „wykrywani” przez użytkowników różnych przeglądarek.

Składnia. Naprawdę pomocne jest użycie kontrolera kodu / IDE / edytora-wtyczki itp. Dla HTML, CSS, JavaScript itp. Korzyść, którą zyskały przeglądarki dzięki możliwości obsługi źle sformułowanego HTML działa przeciwko tobie, gdy różne przeglądarki działają inaczej z to narzędzie niezbędne do sprawdzania formatu HTML. Posiadanie dobrze sformatowanego HTML bardzo pomaga w pozbawionym kodu HTML, ponieważ zły kod powinien mieć lepszą widoczność.

Michael Durrant
źródło
4

Rozwiązaniem, które znalazłem, jest kod deklaratywny. Korzystanie z samego kodu proceduralnego to przepis na kod GUI do spaghetti. Jasne, „specjalny sposób na malowanie widżetu” prawdopodobnie pozostanie kodem. Ale to jest kod izolowany w klasie. Programy obsługi zdarzeń, skróty klawiaturowe, rozmiary okien - wszystkie te niechlujne rzeczy najlepiej deklarować.

MSalters
źródło
4

Jest tu wiele świetnych odpowiedzi.

Jedną z rzeczy, które pomogły mi uprościć kod GUI, jest upewnienie się, że GUI ma swój własny model danych.

Dla prostego przykładu, jeśli mam GUI z 4 polami wprowadzania tekstu, to mam osobną klasę danych, która przechowuje zawartość tych 4 pól wprowadzania tekstu. Bardziej skomplikowane GUI wymagają więcej klas danych.

GUI projektuję jako model - widok. Model GUI jest kontrolowany przez kontroler aplikacji modelu aplikacji - widok - kontroler. Widok aplikacji jest modelem GUI, a nie samym kodem GUI.

Gilbert Le Blanc
źródło
2

Aplikacje takie jak edytor tekstu, edytory grafiki itp. Mają złożone interfejsy, a ich kod nie może być prosty. Jednak w przypadku aplikacji biznesowych interfejs GUI nie musi być tak skomplikowany, ale taki, jaki jest nadal.

Niektóre klucze do uproszczenia GUI to (większość dotyczy .NET):

  1. W miarę możliwości dąż do prostszej konstrukcji. Unikaj fantazyjnych zachowań, jeśli firma nie wymaga tego.

  2. Użyj dobrego dostawcy kontroli.

  3. Nie twórz niestandardowej funkcji kontroli w samym kodzie klienta. Zamiast tego utwórz kontrolki użytkownika, które rozszerzają oryginalną kontrolkę w taki sposób, abyś mógł odzwierciedlić swoje specyficzne zachowania w kontrolkach, a nie w kodzie używanego formularza / strony.

  4. Użyj frameworka (nawet domowego) do obsługi internacjonalizacji, zarządzania zasobami, stylów itp., Aby nie powtarzać tego kodu w każdym interfejsie użytkownika.

  5. Do nawigacji użyj komponentu (lub frameworka).

  6. Twórz standardowe dialogi dla błędów, ostrzeżeń, potwierdzeń itp.

Bez szans
źródło
1

Zastosuj obiektowy projekt do swojego kodu i do tworzenia interfejsu użytkownika:

  1. Oddzielna prezentacja i model Użyj biblioteki / frameworku MV lub napisz własną, aby oddzielić logikę widoku / kontrolera od modelu danych. Cała komunikacja z backendem powinna odbywać się wewnątrz modelu, a stan modelu powinien być zawsze zsynchronizowany z backendem.
  2. Odsprzęganie Jeśli obiekt A wie o obiekcie B, wówczas A może wywoływać metody na B, ale B nie powinien wiedzieć o A. Zamiast tego A może nasłuchiwać zdarzeń z B. To zapewnia, że ​​nie ma zależności cyklicznej. Jeśli Twoja aplikacja zawiera wiele zdarzeń między komponentami, utwórz EventBus lub skorzystaj z frameworku sterowanego zdarzeniami, takiego jak Twitter Flight.
  3. Renderowanie częściowe lub pełne Jeśli twój widok jest tabelą lub listą elementów, możesz pokusić się o utworzenie metod takich jak „dodaj”, „usuń”, aby wstawić / usunąć jeden element do / z kolekcji. Twój kod może łatwo się puchnąć, gdy musisz wesprzeć sortowanie i paginację. Moja rada jest następująca: po prostu zrenderuj cały widok, nawet jeśli nastąpi częściowa zmiana. Co z wydajnością? cóż, jeśli twoja kolekcja jest duża, powinieneś i tak dokonać podziału na strony. Programista WWW: upewnij się, że procedury obsługi zdarzeń są delegowane do elementu głównego widoku, który się nie zmienia.
  4. Model widoku Gdy stan widoku staje się zbyt skomplikowany, aby go utrzymać, na przykład widok tabeli musi śledzić dane wiersza, dane kolumny, kolejność sortowania, aktualnie sprawdzane wiersze (jeśli obsługuje wielokrotne sprawdzanie) itp., Prawdopodobnie powinieneś utwórz obiekt ViewModel dla tych stanów. Twój obiekt View powinien wywoływać setery na ViewModel, jeśli coś zmieni się w interfejsie użytkownika (np. Użytkownik sprawdza wiersz); i powinien reagować na zdarzenie zmiany ViewModel poprzez aktualizację interfejsu użytkownika. Zwykle należy unikać aktualizacji interfejsu użytkownika, jeśli zdarzenie zmiany jest wywoływane przez interfejs użytkownika.

Oto mała, ale nietrywialna aplikacja, która pomoże zilustrować niektóre z moich argumentów. Kod interakcji między schematem a widokiem / modelem można znaleźć tutaj: https://github.com/vanfrankie/pushpopbox

szczery
źródło
0

Chcesz przyjrzeć się koncepcji „wiązania danych” . W ten sposób łączymy elementy interfejsu użytkownika z abstrakcyjnymi elementami modelu w sposób deklaratywny, dzięki czemu elementy modelu są automatycznie synchronizowane z zawartością interfejsu użytkownika. Podejście to ma wiele zalet, na przykład brak konieczności samodzielnego pisania procedur obsługi zdarzeń w celu synchronizacji danych.

Obsługuje wiązanie danych dla wielu platform interfejsu użytkownika, na przykład .NET i Eclipse / JFace .

JesperE
źródło