![]() |
![]() ![]() |
![]() |
![]() ![]()
Post
#1
|
|
![]() Grupa: Zarejestrowani Postów: 1 429 Pomógł: 195 Dołączył: 6.10.2008 Skąd: Kraków/Tomaszów Lubelski Ostrzeżenie: (0%) ![]() ![]() |
Witam, mamy taką często sytuację że w programie graficznym mamy coś takiego jak "Opcje", "Parametry" czy coś w tym stylu. Wpisujemy je np. w różnych inputach, textareach, radio buttonach a potem zapisujemy ustawienia. Dostęp do tych ustawień musi być z całego programu, z każdej klasy, która ich wymaga. Często jest tak że te klasy są gdzieś bardzo głęboko w hierarchii i nieodpowiednim jest pisanie czegoś takiego
Kod //taki glupi przyklad options = UI->getOptions(); UI->dom->parter->pokoj->krzeslo->setOptions(options) Wiem że takie rozw. jak powyżej jest nieeleganckie i łamie zasady programowania obiektowego. Drugim rozwiązaniem może być przechowywanie tych ustawień w klasie statycznej i prosty dostęp za pomocą Opcje::parametr1. Jednak to mimo że wygodne, ponoć też jest niepoprawne (zalatuje zmiennymi globalnymi rodem z C). Jaki jest wg Was najlepszy sposób na przechowywanie i przekazywanie tych danych? Lub ogólniej, jeżeli klasa "krzesło" (z powyższego przykładu) potrzebuje jakiejś danej z klasy "UI", to jak mu ją przekazać w najbardziej elegancki sposób? Pozdrawiam Ten post edytował MateuszS 22.11.2013, 19:27:45 -------------------- O! Zimniok :P
|
|
|
![]()
Post
#2
|
|
Grupa: Zarejestrowani Postów: 205 Pomógł: 43 Dołączył: 5.03.2012 Ostrzeżenie: (0%) ![]() ![]() |
W zmiennych globalnych nie ma nic złego. Naczytałeś się bzdur pseudo-profesjonalistów lub pracowników Google.
W programowaniu w takich językach jak Java / C# nie ma innych metod do tworzenia modułów jak użycie zmiennych statycznych / singletonów. Jedyną alternatywą jest odwrócenie zależności (Dependency Inversion/DI) co jest rozwiązaniem chorym umysłowo jeśli się temu bliżej przyjżeć. To co ja bym zrobił to: W C++ stworzył zmienne globalne "ustawienia" i "ui" i używał ich tam gdzie potrzeba. W Javie stworzył klasy Ustawienia i UI i dał im pola "public static Ustawienia ustawienia" i "public static UI ui" przechowujące pojedyncze instancje tych klas, i w celu dostępu do nich używałbym importów statycznych . Wewnątrz obiektów, które są elementami kompozycji danego modułu odwoływał bym się bezpośrednio do zmiennej globalnej / statycznej. Nie ma czegoś takiego jak nieeleganckie rozwiązanie. Wszystko zależy od skali aplikacji, która musi być przewidziana na samym początku. Przede wszystkim przy aplikacjach stacjonarnych DI się nie sprawdza, chociaż może to być idealne rozwiązanie dla aplickaji internetowych. Jeśli chcesz się jednak męczyć z DI/fabrykami, głupimi frameworkami DI typu Guice to droga wolna. Jeszcze raz - zmienne globalne nie są złe, kiedy się wie, jak ich używać. Przede wszystkim w przestrzeni globalnej mogą leżeć typy wysoko abstrakcyjne, takie jak UI / ustawienia / menedżer zasobów czy obecny plik na którym pracujemy (jeśli zakładać że aplikacja pracuje na jednym pliku w czasie jej wykonywania), natomiast nie prymitywne typy int, float czy string. |
|
|
![]()
Post
#3
|
|
![]() Grupa: Zarejestrowani Postów: 1 429 Pomógł: 195 Dołączył: 6.10.2008 Skąd: Kraków/Tomaszów Lubelski Ostrzeżenie: (0%) ![]() ![]() |
W takim razie chyba najlepszym rozwiązaniem w C++ będzie stworzenie klasy statycznej z polami odpowiadającymi za poszczególne ustawienia oraz metodami set/get. Mój sceptycyzm wynika z tego raczej że do takich ustawień można się dostać z każdego miejsca programu. Ale to chyba najlepsze wyjście. Bo przekazywanie takie jak wcześniej zaprezentowałem, wężyk, to chyba jednak faktycznie mało przejrzyste.
-------------------- O! Zimniok :P
|
|
|
![]()
Post
#4
|
|
Grupa: Zarejestrowani Postów: 205 Pomógł: 43 Dołączył: 5.03.2012 Ostrzeżenie: (0%) ![]() ![]() |
Ale czemu klasy statycznej? Wystarczy że stworzysz jedną instancję normalnej klasy w przestrzeni globalnej programu i wtedy będziesz miał dostęp do niej z każdego miejsca.
Co do wężyków. Jeśli w Twoim przypadku jest "krzesło" z metodą "setOptions" to nie musisz robić takich węzyków, jeśli używasz metody "ciągnięcia" danych zamiast ich "pchania". Chodzi o to, że dane potrzebne krzesłu mógłbyś w momencie tworzenia tego krzesła pobierać właśnie z obiektu globalnego. Poza tym, takie węzyki trochę łamią zasadę enkapsulacji. Chodzi o to, że "włamujesz" się po krzesło przez dom->parter->pokoj. To może być przydatne czasami ale dobrze jest przemyśleć sobie czy takie rozwiązanie jest naprawdę bezpieczne. Dam może inny przyklad, trochę bardziej realny. Mam silnik 3d i występują tutaj obiekty przestrzenne (na przykład kula, stożek, sześcian itp). Każdy z nich ma swoją pozycję w przestrzeni. Powiedzmy, że jest to klasa 'Obiekt3d' , a z niej dziedziczą klasy "Kula", "Stożek" , "Sześcian". Natomiast każda z tych klas przetrzymuje referencję do obiektu klasy "Wektor3", która jest ową pozycją, składającej się z 3 pól "x" , "y" , "z" , które są zmiennymi "float". Najłatwiej byłoby zrobić "kształt->pozycja->x = float" żeby zmienić pozycję obiektu na osi x w przestrzeni 3d. Ale co jeśli ze zmianą pzycji musi się wiązać jakaś inna operacja? Na przykład chcemy żeby przy zmianie pozycji sprawdzone zostało czy ten obiekt nie koliduje z innym obiektem. Jeśli zmienimy jedynie wartość x wewnętrznej referencji do pozycji obiektu wtedy coś takiego się nie stanie. Rozwiązaniem jest wprowadzenie do klasy "Obiekt3d" nowej metody "ustawPozycje(Wektor3 pozycja)" i w niej można wykonywać wymagane operacje - zmienić pozycję używając zmiennej przekazanej do metody i skalkulować kolizje. " Mój sceptycyzm wynika z tego raczej że do takich ustawień można się dostać z każdego miejsca programu." No tak , można sie dostać ale co z tego? ![]() Generalnie tyle mogę powiedzieć z własnego doświadczenia.. mam jesszcze trochę innych, negatywnych takich jak programowanie oparte na zdarzeniach, ale nie polecam - 50% kodu to była obsługa zdarzeń, więc kod nic nie wnoszący do programu. Aha i apropos tego UI. Jest jeszcze jedno rozwiązanie, jesli na prawdę nie chcesz zmiennych globalnych lub po prostu chcesz mieć wiele instancji jednej klasy: poszukaj w internecie na temat wzorca projektowego "Composite". Chodzi o to, ze kazdy element UI przetrzymuje referencje do rodzica, czyli "krzesła zna pokój"" w ktorym się znajduje, "pokój" zna "dom" a "dom" zna "UI" do którego należy więc można w momence dodania obiektu do UI przechwycić to UI z poziomu obiektu dodawanego. Ja tego rozwiązana nie polecam. Jako, że używam raczej Javy - ciągle miałem problemy z pustymi wskaźnikami . Poza tym w rezultacie okazuje się się, że w danym momencie istnieje tylko jeden interfejs użytkownika. A żeby zmienić ten interfejs, wystarczy do zmienne globalnej, trzymającej ten interfejs (UI) przypisać po prostu inny... I nagle mamy zupełnie inne GUI - przydatne przy przechodzeniu pomiędzy ekranami. No i w rezultacie nie wiem czy wszystkie przypadki nie redukują się do pojedynczej instancji. Generalnie kompozycja to trudny temat. Ja radzę nie "włamywać" się w obiekty złożone, co bardziej przypomina trochę "włażenie pod skórę" - hackowanie. Najważniejsza jest jednak kontrola nad tym. W wielu przypadkach obiekty można uznać bardziej za struktury danych. W C++ jest do tego "struct" . Takie Ustawienia w wielu przypadkach mogłyby być po prostu właśnie zwykłą strukturą danych, co odpowiada klasie z upublicznionymi polami. Jednak rzadko z czymś takim mam do czynienia. Warto sobie to przemyśleć zanim napisze się linijkę kodu ![]() |
|
|
![]()
Post
#5
|
|
![]() Grupa: Zarejestrowani Postów: 1 429 Pomógł: 195 Dołączył: 6.10.2008 Skąd: Kraków/Tomaszów Lubelski Ostrzeżenie: (0%) ![]() ![]() |
Dzięki za odpowiedź. Mam kilka wątpliwości
Cytat Chodzi o to, ze kazdy element UI przetrzymuje referencje do rodzica, czyli "krzesła zna pokój"" w ktorym się znajduje, "pokój" zna "dom" a "dom" Ale czy przypadkiem wtedy nie mamy wężyka w drugą stronę? Cytat Ale co jeśli ze zmianą pzycji musi się wiązać jakaś inna operacja? Na przykład chcemy żeby przy zmianie pozycji sprawdzone zostało czy ten obiekt nie koliduje z innym obiektem. Jeśli zmienimy jedynie wartość x wewnętrznej referencji do pozycji obiektu wtedy coś takiego się nie stanie. Rozwiązaniem jest wprowadzenie do klasy "Obiekt3d" nowej metody "ustawPozycje(Wektor3 pozycja)" No tak, pisałem o tym, ale tu mamy dziedziczenie, jest trochę inna sytuacja. Ale nawet gdy nie będzie dziedziczenia to wężyk pozostanie tyle że zakończy się metodą a nie bezpośrednim dostępem do pola. Cytat Aha i apropos tego UI. Jest jeszcze jedno rozwiązanie, jesli na prawdę nie chcesz zmiennych globalnych lub po prostu chcesz mieć wiele instancji jednej klasy: poszukaj w internecie na temat wzorca projektowego "Composite". Piszę na razie w Qt, tam w sumie jest podobny mechanizm, przekazywania rodzica ale to raczej nie rozwiązuje mojego dylematu bo wężyk zostanie. Może przybliżę bardziej mój "problem". Mianowicie mam główną klasę odpowiedzialną za okno. Klasę Ramka, która jest pewnym obszarem tego okna oraz klasę Punkt, która jest obszarem w Ramce (może być kilka punktów w ramce). Oto pseudokod, który to reprezentuje: http://wklej.org/id/1186476/ Jak widać, skoro wszystkie buttony są w klasie GUI, pobierając wartości z tych "inputów", "buttonów", trzeba je tak zapisać aby dostęp do tych ustawień był z całej aplikacji. Metody, które znam to tylko wężyk ramka->punkt->setUstawienie(ustawienie) lub klasa statyczna (oraz zaproponowana przez Ciebie klasa z dostępem globalnym). Ale może moja koncepcja jest zła? Wydaje mi się że słusznie nie zastosowałem tutaj dziedziczenia, bo jak napisał pan Grębosz w swojej książce, dziedziczenia używamy gdy jedna klasa jest jakimś podtypem innej a nie gdy jedna zawiera się w drugiej. -------------------- O! Zimniok :P
|
|
|
![]()
Post
#6
|
|
Grupa: Zarejestrowani Postów: 205 Pomógł: 43 Dołączył: 5.03.2012 Ostrzeżenie: (0%) ![]() ![]() |
Cytat Cytat Chodzi o to, ze kazdy element UI przetrzymuje referencje do rodzica, czyli "krzesła zna pokój"" w ktorym się znajduje, "pokój" zna "dom" a "dom" Ale czy przypadkiem wtedy nie mamy wężyka w drugą stronę? Właśnie nie. Zapomniałem powiedzieć o kilku sprawach. Wyobraź sobie taki układ klas (pisane łamaną Javą):
Chodzi o to, że teraz każdy obiekt może wywołać UI, kiedy tego będzie potrzebował, jedynym warunkiem jest to, że gdzieś w tej hierarchii obiektów w kompozycji musi być obiekt typu ui, bo na przykład gdyby użyć tego kodu:
Więc tutaj nie ma wężyków i nie ma modułu w globalnej przestrzeni. Co do twojej koncepcji i Qt - nie znam Qt ale pytanie brzmi: kiedy będziesz potrzebował dostępu to ustawień? Chodzi o to, ze mógłbyś nadpisać metody, a pewnie takie są, które są wykonywane w momencie dodawania elementów do okna lub rodzica - kontenera lub podczas wyświetlania. I w nich wykonywać kod odpowiedzialny za odczytywanie ustawień. W twoim przypadku punkt musiałby dziedziczyć po czymś takim ja QWidget i nadpisać jakąś metodę, która jest wywoływana w momencie dodania/wyświetlenia tego punktu - jaka to metoda - nie wiem - to znajdziesz pewnie w dokumentacji. I jeszcze jedna prosta rada: jeśli masz n punktów w ramce, która jest w okienku to jeśli potrzebujesz iterować te punkty.. nie rób tego z poziomu okna. Czyli nie rób czegoś takiego: W klasie "Okno" zawierającej pole "ramka" for(int i = 0 ..... i++) { punkt = ramka->punkt[i]; ... operacje na punkcie ... } postaraj się takie iteracje i tym samym wszystkie operacje zawrzeć w klasie która zawiera punkty bezpośrednio: W klasie Ramka: void metoda() { for(int i = 0 ..... i++) { punkt ->punkt[i]; ... operacje na punkcie ... } } W klasie Okno: ramka->metoda(); |
|
|
![]() ![]() |
![]() |
Aktualny czas: 20.08.2025 - 11:09 |