Programowanie obiektowe - próba logicznego doprecyzowania metod dla klas

Postanowiłem iść dalej w kodzie mimo niepewności co do “odpowiedzi” zawartej w tytule tematu tzn. nierozwiązania tego problemu jasno i wyraźnie, ale temat utworzę, bo jestem ciekawy co Wy o tym sądzicie.

 

Mamy sobie jakąś listę w której znajdować będą się jakieś elementy np. zabawne frazy, gdzie pojedyncza fraza = jeden obiekt odpowiadający klasie “Fraza”. Lista jest obiektem i też wynika z klasy “Lista” lub “Grupa”, jak kto woli. Jaki widzę problem?

 

Jeżeli chcemy dodać nową frazę do istniejącej listy to należy:

 

  • napisać metodę “Lista.dodajFrazę”, ponieważ lista ma możliwość dodania do siebie nowych fraz, a więc operujemy na niej (liście)

  • czy może napisać metodę “Fraza.dodajDoListy”, ponieważ dodajemy tę frazę do listy, a więc operujemy nią (frazą), choć nie na niej.

 

W pierwszym przypadku zakładam, że proces tworzenia frazy odbywa się podczas wykonywania metody “dodajFrazę” z klasy “Lista”, natomiast w drugim oczywiście - że fraza jest już w jakiś sposób utworzona, tylko nieprzypisana do listy. Gdyby natarczywie i zbyt przesadnie przenieść to na rzeczywistość, to jak wiadomo - na listę dodajemy nową frazę czy cokolwiek innego tworząc ją przy tym, bo wcześniej znajduje się ona tylko w naszej głowie (niematerialnie). Co o tym sądzicie?

 

Zapraszam do dyskusji :slight_smile:

Byłbym bardziej skłonny wykorzystać dodajFrazę, wzorując się na STL, wtedy byłoby to zgodne z przyzwyczajeniami i łatwiejsze do zainplementowania (jeżeli używamy STL).

Wyłącznie pierwsze rozwiązanie, przy czym nie chciałbym również, żeby to klasa listy posiadała logikę tworzenia frazy. Fraza jako odrębny byt powinna być sobie spokojnie tworzona gdzieś obok i na gotowe dodawana do listy. Ewentualnie (nie napisałeś, w czym piszesz, ale w większości języków OO to możliwe) do Lista.dodajFrazę możesz przekazywać wywołanie konstruktora frazy. 

Dzięki za Wasze odpowiedzi. Upewniłem się tylko, że w dobrym kierunku myślę, bo identycznie sądzę. Chodzi mi o JS, ale w nim ma to także zastosowanie :slight_smile: Dzięki też za rozszerzoną wypowiedź Frankfurterium , która rozpatruje trochę szerzej sposób rozwiązania problemu w kontekście większości języków. Dla mnie pożądane jest właśnie takie rozwiązanie, a nie ograniczanie się do jednego języka :slight_smile: Zapytam Ciebie tylko - czemu sądzisz, że wyłącznie to pierwsze? Drugie miałoby katastrofalne skutki, czy po prostu byłoby nieco gorsze?

Bo właśnie po to wymyślono koncept klasy. Całą logikę operacji na liście trzyma się w klasie reprezentującej listę, a nie rozsiewa po różnych zakątkach systemu. W ten sposób łatwiej ją znaleźć, zrozumieć i zmienić. Z tego samego powodu lista nie powinna składać do kupy frazy. Lista jest od operacji na liście, fraza jest od tworzenia i modyfikacji frazy. Lista powinna wiedzieć o frazie tak mało, jak tylko się da (że istnieje i ma ją przyjąć). Fraza nie powinna wiedzieć o liście nic, bo kiedyś może zostać włożona do innej struktury (mapy, drzewa), a wtedy nie obeszłoby się bez refactora. 

 

I to nie jest jakieś moje widzi mi się. Przez dekady używania OOP wytworzyło się sporo wzorców, dobrych praktyk i praw, jak np. zasada pojedynczej odpowiedzialności, prawo Demeter i inne podobne. Na rynku jest parę dobrych książek o tym traktujących, ale całą potrzebą wiedzę znajdziesz w różnych artykułach opisujących “OOP Best Practices”. Problem leży w tym, że początkującemu programiście ciężko dostrzec ich sens, a wszystko klaruje się wraz z nabywanym doświadczeniem.

ale ja rozumiem o co Ci chodzi i także znacznie odpowiedniejszym rozwiązaniem wydaję mi się sposób pierwszy. Mimo to ciekawy byłem, co i kto tu napisze. Właśnie dlatego, że operujemy na liście to wg mnie owa lista powinna mieć metodę od dodawania do niej fraz. Specjalnie zaznaczyłem przy drugim rozwiązaniu, że choć operujemy frazą, to nie operujemy na niej tylko na liście i dla mnie jest to poniekąd kluczowa sprawa. Jednak druga możliwość wydaje się też realna i niekoniecznie jakaś zła, stąd cały ten temat. Byłem ciekawy, czy znajdą się tacy udzielający w temacie, którzy biorą pod uwagę drugą możliwość np. w konkretnych przypadkach. Dzięki za odpowiedź :slight_smile:

Trudno coś dodać do listy nie wykonując na niej jakichś operacji (czyt. nie da się), więc w gruncie rzeczy wywołując drugą funkcję wywołujesz obie.

 

Właściwie, to najwydajniej byłoby, gdyby lista zawierała wskaźniki. Wtedy nie trzeba kopiować frazy, ani jej tworzyć.

Klasa lub szablon czegoś tak prostego jak lista jest najprawdopodobniej dostępna w języku czy standardowych bibliotekach do niego, więc nieczęsto trzeba te metody pisać, a jak coś jest to się to wykorzystuje. 

Ogólnie:

  • Jeśli masz dowolny kontener, gdzie potrzebujesz możliwości wydobycia każdego elementu, dodajesz element do kontenera, a więc opcja 1. 

  • Jeśli potrzebujesz tylko np. sprawdzać/wyświetlać dla elementu, do których grup (w sensie podzbiorów jakiegoś zbioru) on należy, prawdopodobnie nie potrzebujesz do tego rozwiązania kontener=grupa.

- Jeśli masz zapotrzebowanie na obie powyższe możliwości i twierdzisz, że w tym celu zarówno kontener musi znać swoje elementy, jak i element kontenery, do jakich należy, prawdopodobnie się mylisz :smiley:

Co do ostatniej opcji  - oczywiście da się napisać swoje klasy w ten sposób, ale część osób nie bez powodu nazwie to ‘spaghetti code’, i o ile nie optymalizujesz złożoności obliczeniowej kosztem złożoności pamięciowej, najczęściej nie warto w ten sposób łamać ładnej, hierarchicznej, drzewiastej struktury programu.

@ktoś tam  dobre spostrzeżenie, choć bardziej chodzi mi o ogólną koncepcje.

 

@pio_95  dobry post. Rozjaśnia nieco sprawę tzn. pokazuje kilka różnych przypadków. Czyli np. drugi z wymienionych przez Ciebie sposobów to taki, gdzie jakaś fraza należy do przynajmniej jednej grupy opisującej ją. Np. jest sobie fraza należąca do grup opisujących “Zabawne” oraz “Inteligentne”. Coś jak tagi, zgadza się? Wtedy zdecydowanie można rozpatrywać drugie rozwiązanie, zamiast pierwszego, czyli “Fraza.dodajDoListy”?

 

Nadal nie przepuściłbym tego przez code review. Jako absolutnie jedyne usprawiedliwienie drugiej metody widziałbym jakiś ogromny przyrost wydajności, w innym wypadku to oznaka skopanego designu.

No tu akurat przykład podany przez pio_95  wydaję mi się bardzo rozsądny. Tagów może być pełno. Oczywiście przy założeniu, że te listy jako obiekty będą w ogóle istnieć, bo (chyba) można na różne sposoby rozwiązać ten problem. Tagi też w tym przypadku pełniłyby głównie funkcje opisową, a wtedy już nie mają aż tak dużego znaczenia jak np. podstawowa lista elementów czegoś w jakiejś aplikacji.

Tag jako lista trzymająca frazy? Coraz większe aberracje… :?  Poczytaj o tych dobrych praktykach, zamodeluj własne pomysły UML-em. 

Over and out.

Akurat czytania to mam dość - zdecydowanie go za dużo, od lat. Natomiast praktyka w kodzie, czy modelowanie, choć niekoniecznie w UMLu - to dobra rada. Podałem założenie. Możesz wstawić inne słowo niż “Tag”. Wiem co masz na myśli, ale nie chodzi mi o rozpatrywanie czy lista jest dobra dla tagu, czy nie. Zamiast tagu wstaw sobie nie wiem, np. kategoria i załóż, że jest multum możliwych kategorii, które służą jedynie do opisywania fraz pojedynczymi wyrazami, jak np. wyrazem “Zabawne”. Dodaj też do tego, że fraza to główny element aplikacji. Rozumiem, że w tym przypadku dalej obowiązuje to co piszesz, ale teraz brzmi raczej trochę mniej… “aberracyjnie” :wink:

A dlaczego nie przeszukiwać jednego całego zbioru elementów w poszukiwaniu tych z odpowiednimi cechami (tagi / kategorie / cokolwiek), gdy ich potrzeba? Rozwiązanie optymalne pamięciowo, a trzeba sporo się postarać zarówno o liczebność zbioru, jak i złożoność warunków, po których szukasz, aby  poprawki na wydajność miały przynieść wymierne korzyści.

@pio_95  nie widzę przeszkód. Takie rozwiązanie najbardziej mi pasuje, bo przecież jedna właściwość opisowa nie jest warta tworzenia obiektu :P Można też raczej założyć, że każda fraza ma w sobie wewnątrz obiekt “tagi” i tam je przechowywać po prostu. Zresztą chyba  Frankfurterium  miał na myśli właśnie coś w ten deseń, ale celowo pomijałem taką możliwość, co wyjaśniłem wyżej.

 

A co mają wskaźniki do kopiowania fraz? o.O

Gdy rozmawiamy o koncepcjach, skupiajmy się na koncepcjach, a nie szczegółach implementacyjnych czy konstrukcji dostępnych w konkretnych językach.

 

 

Jeśli potrzebujesz mieć jeden obiekt w dwóch listach, to zdecydowanie dodajesz go do dwóch list, a nie łamiesz nie tylko SOLID, ale i tak podstawowe zasady, jak enkapsulacja.

 

 

Niby dlaczego? Klasy tworzy się, gdy chcemy zdefiniować zbiór tworów o wspólnym zachowaniu, nie ma znaczenia ilość danych w nich przechowywanych.