Autor Wątek: [c++] Co lepsze? Menedżer, interfejs czy kilka obiektów tego samego typu?  (Przeczytany 10929 razy)

Offline Xenox93

  • Użytkownik

# Marzec 24, 2014, 17:25:48
1.
Cytuj
Po dziada ci ten manager?
Co on ma robić wg. ciebie.

Do takiego samego stwierdzenia doszedłem, ale odnośnie samego bytu jakim jest Manager.

2.
Cytuj
Moim zdaniem tworzysz problem nie rozwiązując żadnego chcąc zastosować coś takiego...

Hmm... też nad tym myślałem, ale stwierdziłem, że skoro większość pisze Manager'y to znaczy, że powinienem pójść tym tropem. W ogóle sam sposób w jaki tworzę kod wygląda na hybrydę.

a) Na ogół tworzę strukturę, która opisuje dany obiekt i deklaruję jako zmienną, która z kolei trzyma kilka obiektów( tekstury, okna( czyt. tylko struktury opisujące ten obiekt ) ), czy to za pomocą kontenera vector czy za pomocą map.
b) Później piszę metody, które wykonują działania na strukturze.

Tak wygląda mam jedną jedyną klasa, którą deklaruję. Dopiero za jej pomocą operuję na elementach.

3.
Hmm... kwestia zaprojektowania kodu, tym razem nie wydaje się łatwa. Zawsze kod miałem w pamięci, już tworzył się w głowie, ale gdy za każdym razem chcę rozwinąć kod, funkcjonalność to muszę napisać wszystko od nowa.
Stąd właśnie chęć napisania framework'a, na początek dla własnego użytku.
Moim celem jest zaprojektować tak kod, aby zminimalizować szansę na ponowne pisanie tego samego. Wiem również, że nie da się napisać raz porządnego szkieletu/kodu, który wystarczy na całe życie, ale mam już dość ciągłego ponawiania tego procederu. Nie mogę również zrezygnować z danej funkcjonalności, bo wiem, że mi się przyda i ułatwi w przyszłości programowanie( np. projektowanie UI za pomocą skryptu ), a nie jest czymś niemożliwym.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline ArekBal

  • Użytkownik

  • +1
# Marzec 24, 2014, 19:20:58
Hmm... też nad tym myślałem, ale stwierdziłem, że skoro większość pisze Manager'y to znaczy, że powinienem pójść tym tropem. W ogóle sam sposób w jaki tworzę kod wygląda na hybrydę.
Skąd ten pomysł?

Cytuj
Na ogół tworzę strukturę, która opisuje dany obiekt i deklaruję jako zmienną, która z kolei trzyma kilka obiektów( tekstury, okna( czyt. tylko struktury opisujące ten obiekt ) ), czy to za pomocą kontenera vector czy za pomocą map.
To jest nic nadzwyczajnego, "dzień jak co dzień"... i nie potrzebujesz do tego "managera"...
Wystarczą do tego funkcje-helpery na ten przykład albo nadrzędna klasa może to trzymać.

W ogóle w każdym przypadku wypadałoby się zastanowić nad sensem takiej enkapsulacji.

Opakowywanie vectora np. uniemożliwia/utrudnia korzystanie z emplace.
Po za tym gdzie są granice tego opakowywania?
Tylko po to opakować, by potem wszystkie metody wystawić?

Jeśli jedynym celem takich zabiegów jest ładniejsze API to czasami lepiej sobie odpuścić.

Dużo wydajniejszym, szybszym do zakodzenia i bezpieczniejszym wyjściem będzie utworzenie funkcji statycznych biorących referencje do takiego vectora i tyle.

Pogódź się z tym inaczej będziesz marnować czas na odpowiadanie na takie pytania:
Zwracać przez wartość/move czy modyfikować referencje?
To const or not to const?
Metoda wirtualna, traits czy delegat?
Implementować Rule of 6?
Thread safe czy nie?
Exception safe czy nie?

Na żadne z tych pytań, nie ma prostych odpowiedzi i każdy przypadek trzeba rozważać oddzielnie.

Dla mnie "ładne" API jest za szybko tworzącym się, szybkim oraz wygodnym w użyciu. W tej kolejności.

Cytuj
Stąd właśnie chęć napisania framework'a, na początek dla własnego użytku.
Moim celem jest zaprojektować tak kod, aby zminimalizować szansę na ponowne pisanie tego samego.
Utopia.
Nic nowego w tej dziedzinie nie wymyślisz. Kiedyś można było wymyśleć shared_ptr i kilka innych klas dziś obecnych w stl czy booście. Dziś trudno coś nowego "ułatwiającego" dopisać do tego. Lepiej się skup na domenie i tam spróbuj coś dobrego zrobić.

To właśnie podejście pod tytułem "Moje idealne API" prowadzi do pisania kolejnego niepotrzebnego wrappera "over and over". Jakiś czas temu doszedłem do wniosku że wzorzec Wrapper jest drugim po Singletonie szatanem programowania.
« Ostatnia zmiana: Marzec 24, 2014, 19:31:41 wysłana przez ArekBal »

Offline Xenox93

  • Użytkownik

# Marzec 24, 2014, 21:40:03
1.
Cytuj
Cytuj
Hmm... też nad tym myślałem, ale stwierdziłem, że skoro większość pisze Manager'y to znaczy, że powinienem pójść tym tropem. W ogóle sam sposób w jaki tworzę kod wygląda na hybrydę.
Skąd ten pomysł?

Hmm... może dlatego, że wcześniej ktoś zwrócił mi uwagę, że Manager, tylko przechowuje obiekt i posiada metody( dodanie, usunięcie, pobranie ), a dopiero druga klasa może posiadać metody, które robią coś więcej z obiektem( zmiana rozmiaru itp. ). Więc moja klasa posiada oba te cechy w jednej klasie.

2.
Cytuj
To jest nic nadzwyczajnego, "dzień jak co dzień"... i nie potrzebujesz do tego "managera"...
Wystarczą do tego funkcje-helpery na ten przykład albo nadrzędna klasa może to trzymać.

Tylko jak takie funkcje miałyby wyglądać? Dodatkowo stosowanie samych funkcji nie jest podobne do C lub programowania strukturalnego? Nadrzędna klasa, czyli dziedziczenie, tak?

Ogólnie chcę utworzyć kilka klas, załóżmy, że będą to: Window, Graphic( inaczej Render ), Texture itp.

Window-> powinien mieć metody: Create( tworze kolejne okno, parametry opisują okno ), Remove, Get( pobiera okno( uchwyt ) lub opis okna( np. rozdzielczość ) ), Resizeitp.

Graphic-> posidać powinien metody: Initialize( tworzy kontekst ), Destroy, Get( pobiera parametry renderowania, np. rozdzielczość ), Set, Render( jako parametr mogę podać jeden obiekt do wyrenderowania lub kontener, który wyrenderuje się ) itp.

Texture-> posiada z kolei takie metody: Add, Remove, Get

Jak widać, każda klasa posiada odmienne metody. Dodatkowo każda klasa jest tylko raz zadeklarowana. Dopiero za jej pomocą możemy dodać okno, teksturę, wyrenderować coś przekazując obiekt, np. mając klasę Scene, mogę przesłać daną scenę do funkcji Render, która sama się zajmie renderingiem w zależności od użytego shader'a, tekstury lub oddalenia od kamery ;)

Raczej takie coś nie jest utopijne i dałoby się zrobić to w sposób wydajny lub szybko wykorzystać w każdym projekcie. Czyli jakby nie patrzeć każda klasa Window, Graphic, Texturemoże trzymać w sobie obiekt opisujący i posiadający teksturę, okno itp. lub odnosić się do zewnętrznego obiektu, ale klasa operująca na obiektach powinna być jedna, bo po co tworzyć nowe okno z tymi samymi metodami, np. po co każdy obiekt okna ma posiadać metody tworzące okno, usuwające lub zmieniające rozdzielczość ;) Czy nie jest tak?

Bo dziwnie wygląda dla mnie coś podobnego:
a)
Window editor, render;
Graphic graphic;

Material trawa, drewno;
Model pole_1, pole_2;
Scene pola, dom;

editor.Create( ... );
editor.AddButton( ... );
[...]

render.Create( ... );

graphic.Initialize( render.Get() );

trawa.Add( ... );
drewno.Add( ... );

pole_1.Add( ... );
pole_2.Add( pole_1 );

pola.AddObject( pole_1.get(), trawa.get() );
dom.AddObject( ... );

while( ESC lub Close )
{
      // Ustawianie tekstury, shader'a itp.
      graphic.render( pola.pole_1 );
      graphic.render( pola.pole_2 );
      [...]

      graphic.render( dom.krzeslo_1 );
      graphic.render( dom.krzeslo_2 );
      [...]
}

Ten poniższy kod wygląda bardziej przyjaźnie:
b)
Window windows;
Graphic graphic;

Material materials;
Model models;
Scene scenes;

windows.Create( "Editor", ... );
windows.Get()["Editor"].AddButton( ... );
[...]

windows.Create( "Render", ... );

graphic.Initialize( windows["Render"] );

materials.Add( "Drewno", ... );
materials.Add( "Trawa", ... );

models.Add( "Pole_1", ... );
models.Add( models.Get()["Pole_1"], "Pole_2", ... );

scenes.Add( "Pola" );
scenes.AddObject( "Pola", "Pole_1", "Trawa", ... );

scenes.Add( "Dom" );
[...]

while( ESC lub Close )
{
      // Ustawianie tekstury, shader'a itp.
      graphic.render( scenes.Get() );
}

Tak teraz się zastanowiłem jak przebiegała droga i dlaczego piszę w ten sposób kod. Najpierw pisałem program tak jak ten w podpunkcie a). Wydało mi się to dość toporne, nie potrzebnie robiłem tyle deklaracji dla byle jednego obiektu, a jak pomyślałem, jakby miał wyglądać kod dla gry, gdzie jest pełno obiektów to się złapałem za głowę. Własnie na pomoc przyszły mi kontenery, a najbardziej vector ;) Bardzo mi to ułatwiło programowanie, ale nadal pojawiały się problemy z dodaniem różnych feature'ów, np. skryptu. Wszystko to zmieniało się aż do obecnej formy, kod widzicie w podpunkcie b) ;)

Dodatkowo powyższy kod ma na celu zoptymalizowanie grafiki i pamięci. Np. warto stworzyć oddzielnie obiekty: meshes, textures, shaders itp. Bo całkowitym OverKill'em jest dodawanie tego samego obiektu oddzielnie do różnych scen ;) Np. po co dodać krzesło pierwszy raz do sceny - dom, a później drugi raz do sceny - spiżarnia?
Material materials;
Scene scenes;

Texture textures;
Shader shaders;
Mesh meshes;

meshes.Add( "Krzeslo", ... );
meshes.Add( "Stol", ... );
meshes.Add( "Krzeslo", ... ); <- drugi raz się nie doda ;P

textures.Add( "Drewno", ... );
shaders.Add( "Drewno", ... );
materials.Add( "Drewno"/* nazwa materiału */, textures.Get()["Drewno"], shaders.Get()["Drewno"] );
[...]

scenes.Add( "Jadalnia" );
scenes.Get()["Jadalnia"].AddObject( "Krzeslo", materials.Get()["Drewno"] );
[...]

3.

Cytuj
Cytuj
Stąd właśnie chęć napisania framework'a, na początek dla własnego użytku.
Moim celem jest zaprojektować tak kod, aby zminimalizować szansę na ponowne pisanie tego samego.
Utopia.
Nic nowego w tej dziedzinie nie wymyślisz. Kiedyś można było wymyśleć shared_ptr i kilka innych klas dziś obecnych w stl czy booście. Dziś trudno coś nowego "ułatwiającego" dopisać do tego. Lepiej się skup na domenie i tam spróbuj coś dobrego zrobić.

Hmm, no wiesz, ja nie mam na myśli napisać czegoś super wydajnego, konkurującego z dotychczasowymi API. Nawet nie chcę pisać Wrapper'a choć właśnie chyba go piszę, bo kiedyś zapoznając się z tym terminem zauważyłem, że nie świadomie piszę kod wyglądający na wrapper'a :/

W sumie pisać coś łatwego do zaprogramowania zawsze jest warto. Dlatego powstały różne języki, skrypty, API itp.

Mi chodzi raczej o napisanie framework'a niż API.
Np. dołączam do nowo utworzonego projektu plik dll swojego projektu i mogę skupić się na problemie niż zajmować czas na utworzenie i obsłużenie okna.
Tak samo z  grafiką, np. wystarczy, że chcę zobrazować coś na wykresie lub zobrazować dany wzór matematyczny, to sięgam po swój framework, dołączam go i wydając kilka poleceń mam już wszystko gotowe.
Chcę użyć OpenCL, nie ma sprawy, inicjalizuję moduł obliczeń GPGPU i pisze do tego jakiś zewnętrzny skrypt lub pisze wzór do shader'a bądź Geometry Shader i wszystko gotowe.

Ile by mi to zajęło jakbym miał wszystko napisać od nowa? Pewnie kilka dni, zamiast jeden dzień :/ Jeden dzień poświęciłbym na utworzenie okna i zainicjalizowanie grafiki, a to wszystko ustawiane za pomocą skryptu. A kolejny na resztę lub tydzień na znalezienie znowu błędów, które nie zdarzyłyby mi się przy użyciu autorskiego framework'a.

Oczywiście biorę pod uwagę, że wstaję ok. 8:00 lub 10:00, a idę spać mniej więcej o 23:00. Jednakże, gdybym miał pisać powyższy program po godzinach roboczych, to wszystko trwałoby tydzień lub więcej, a przecież chcę napisać tylko głupi program obrazujący funkcję matematyczną. Mógłbym użyć Qt, ale i ta biblioteka ma ogromne skazy. Dodatkowo nie korzysta z natywnych bibliotek dostępnych przez system, WinAPI wygrywa wydajnościowo z następcą, który został napisany obiektowo ;) Mój framework nie jest czymś napisanym od zera, po prostu jedna klasa, która tworzy okno za pomocą WinAPI. Później jedna klasa dla OpenGL, tak samo dla dźwięku itd. itd.

Później planuję napisać rozszerzenie, dodając dyrektywy prekompilatora, tak aby utworzyć okno także na Linuksie ;) Ot cały mój framework, moje ułatwienie ma wyglądać na bardziej przyjazny sposób tworzenia elementów( przyjazny dla człowieka ). Nie jakieś glClear( ... ); glBegin(); [...] glEnd(...);, a bardziej graphic.render( scene ); i po sprawie. Dałbym również niskopoziomowy dostęp do sprzętu na tyle, na ile OpenGL pozwala.

Nie zamierzam aktualnie pisać nowego OpenGL lub DirectX :D

Offline Xirdus

  • Redaktor

  • +1
# Marzec 25, 2014, 00:59:34
Piszę z komórki więc krótko.

Klasa Texture nie posiada danych na temat tekstury. Ona JEST danymi tekstury.

To, co nazywasz wielokrotnymi deklaracjami metod, to tylko wielokrotne wywołanie tej samej metody na różnych obiektach po kolei - coś, co jest najzupełniej normalne, i świadczy o tym że te metody rzeczywiście są potrzebne.

Pisząc w gołym WinAPI nie dostajesz z automatu więcej wydajności. Trudniej nie znaczy lepiej. Pamiętaj że Qt wewnętrznie również korzysta z WinAPI.

Offline ArekBal

  • Użytkownik

# Marzec 25, 2014, 01:23:06
czemu meshes nie używają Meshes... albo MeshContainer nawet jeśli...

Kiedy zamierzasz kasować okna, meshe, wraz z całym kontenerem? Nie zawsze oto chodzi...
Nie widzę do czego twój kod służy?
W czym twój kod jest lepszy niż dzikie metody na mapie, vectorze?
Ładniejsze API? Coś jeszcze?

Offline Xenox93

  • Użytkownik

# Marzec 25, 2014, 11:48:16
1.
Cytuj
Klasa Texture nie posiada danych na temat tekstury. Ona JEST danymi tekstury.

Tak, ale można użyć klasy, która dodaje teksturę za pomocą loader'a. Dodatkowo szukanie danej tekstury za pomocą nazwy, np. używając kontenera map.

2.
Cytuj
To, co nazywasz wielokrotnymi deklaracjami metod, to tylko wielokrotne wywołanie tej samej metody na różnych obiektach po kolei - coś, co jest najzupełniej normalne, i świadczy o tym że te metody rzeczywiście są potrzebne.

Ogólnie rzecz biorąc, nie ma sensu, chyba, że tworzymy klasy statyczne. Więcej klas z tymi samymi metoda, powoduje, że więcej trzeba zmarnować na pamięć, co jest niepotrzebnym marnotractwem( nie jest to mikrooptymalizacja ).

3.
Cytuj
Pisząc w gołym WinAPI nie dostajesz z automatu więcej wydajności. Trudniej nie znaczy lepiej. Pamiętaj że Qt wewnętrznie również korzysta z WinAPI.

Też tak kiedyś myślałem, ale gdy napisałem aplikację w czystym WinAPI i w Qt oraz utworzyłem kontekst OpenGL okazało się że różnica pomiędzy tymi API wynosiła aż 2 000 FPS, to jest dużo. Można by to spożytkować na coś innego, nie bawiąc się w mikrooptymalizacje.

4.
Cytuj
czemu meshes nie używają Meshes... albo MeshContainer nawet jeśli...

Na ogół tworzy się klasę nazywając ją liczbą pojedyńczą, dopiero obiekt klasy można nazwać l. mnogą jeśli zawiera w sobie kilka tych samych elementów.

5.
Cytuj
Kiedy zamierzasz kasować okna, meshe, wraz z całym kontenerem? Nie zawsze oto chodzi...

Do tego właśnie służy metoda Remove, w której jako parametr podaję nazwę okna do usunięcia. A chcąc wyczyścić wszystko mogę użyć własnej metody Clear.

6.
Cytuj
Nie widzę do czego twój kod służy?
[...]
Ładniejsze API? Coś jeszcze?
Właśnie ku temu, aby był czytelny. Jeżeli utworzę tysiąc obiektów klasy Window, nie wygląda to intuicyjnie, ładnie, wprowadza nieład/chaos.
Window window1, window2, window3, window4, window5, window6, window7, ..., windown;
7.
Cytuj
W czym twój kod jest lepszy niż dzikie metody na mapie, vectorze?
Właśnie nie zamierzam zamieniać tych kontenerów, a opakować je w klasy lub utworzyć jakieś statyczne funkcje, które będą operowały na tych obiektach ;)

Offline Ivian

  • Użytkownik
    • Ivian's Cave

  • +1
# Marzec 25, 2014, 17:12:54
Przerzuć się na C# ;) I tak M$ wcześniej czy później oznaczy WinApi jako obsolete. Skoro nawet DX piszą od nowa. A jednak WinApi to większy crap niż dx.

Edit: Dopiero teraz doczytałem coś o multiplatformie.

Offline Xenox93

  • Użytkownik

# Marzec 25, 2014, 19:38:41
@Ivian: Dzięki chociaż za chęci ;) Dokładnie, zależy mi na multiplatformowości, to właśnie spowodowało, że z DX przeniosłem się na OGL'a.

1.
Hmm... po ogólnym zastanowieniu, doszedłem do wniosku, że nie ma co deklarować zmiennych w klasach, więc mogę utworzyć klasę Manager lub poradzić sobie nawet bez niej. Następnie utworzyć klasę z funkcjami statycznymi(?) lub jakieś funkcje globalne umieszczone w przestrzeni nazw, które z kolei wykonują/manipulują danymi, dopiero tam piszę kod dodający tekstury, tworzący okna itp.

Czy to jest już lepsze rozwiązanie? Tylko, że chciałbym nauczyć się nawet czegoś nowego, np. dziedziczenie, polimorfizm, interfejs itp. tak aby w przyszłości nie pisać od nowa kodu, bo nagle się okaże, że taki kod wygląda chaotycznie lub w najgorszym przypadku nie da się połączyć tego z jakąś nową funkcjonalnością, np. skryptami :/

BTW, rozwiązanie, które podaliście ma jeden problem, gdzie deklarować zmienne? Na ogół robiłem to w klasach lub gdzieś ukrywałem, ale jeśli mam tworzyć zmienne globalne lub w funkcji main to nie uśmiecha mi się. Co jeśli będę miał GUI i nawet jakąś najprostrzą grę 2D, np. szachy, nagle okazuje się że kod jest tak niespójny, chaotyczny, że ciężko znaleźć błąd lub co dana funkcja robi.

Offline ArekBal

  • Użytkownik

# Marzec 25, 2014, 22:11:27
Gdzieś mi posta wcięło(wywaliło internet?)
Nie miałem siły od nowa to pisać ale spróbuje w skrócie:
Opadły mi ręce jak zobaczyłem ze się upierasz przy tych menedżerach zamiast korzystać z klas std wprost.
Nie podoba mi się twoje używanie stringów jako identyfikatorów w kodzie...

Jak będziesz potrzebować zadeklarować zmienną to dwa razy się zastanowisz gdzie to zrobić... czy  trzymać na stosie i puścić jako argument do funkcji, czy jakaś funkcja go konstruuje, czy rzeczywiście? Czy rzeczywiście potrzebuje caaały czas tego stanu, czy może wystarczy klasa przechowująca jakiś stan tymczasowo która tylko przechowa pomiędzy wywołaniami.

Nie widzę jaaakież to dane miałby przechowywać kontener które nie mogłyby się znaleźć w tej samej klasie gdzie kontener jest używany. Jakieś przykłady gdzie taka enkapsulacja byłaby uzasadniona praktycznymi względami?
« Ostatnia zmiana: Marzec 25, 2014, 22:13:41 wysłana przez ArekBal »

Offline Xirdus

  • Redaktor

# Marzec 25, 2014, 22:48:57



2.
Ogólnie rzecz biorąc, nie ma sensu, chyba, że tworzymy klasy statyczne. Więcej klas z tymi samymi metoda, powoduje, że więcej trzeba zmarnować na pamięć, co jest niepotrzebnym marnotractwem( nie jest to mikrooptymalizacja ).
Po pierwsze: mylisz klasy z obiektami.

Po pierwsze: metody występują tylko jeden raz w całej binarce, nawet jak masz pierdylion obiektów - bo metoda nie różni się niczym (tak, NICZYM) od zwykłej funkcji, poza tym że ma implicite dodatkowy argument (this). Jeśli nie znasz takich kompletnych podstaw, nie bierz się za optymalizacje, bo nie tylko twoje rozwiązanie jest o wiele cięższe w użytku od standardowego, ale też potencjalnie wolniejsze.

3.
Też tak kiedyś myślałem, ale gdy napisałem aplikację w czystym WinAPI i w Qt oraz utworzyłem kontekst OpenGL okazało się że różnica pomiędzy tymi API wynosiła aż 2 000 FPS, to jest dużo. Można by to spożytkować na coś innego, nie bawiąc się w mikrooptymalizacje.
Po pierwsze: http://www.asawicki.info/news_1058_dlaczego_fps_jest_do_bani.html

6.Właśnie ku temu, aby był czytelny. Jeżeli utworzę tysiąc obiektów klasy Window, nie wygląda to intuicyjnie, ładnie, wprowadza nieład/chaos.
Window window1, window2, window3, window4, window5, window6, window7, ..., windown;
Po pierwsze: naucz się korzystać z tablic.

Po pierwsze: w co najmniej 99% gier jest jedno okno.

7.Właśnie nie zamierzam zamieniać tych kontenerów, a opakować je w klasy lub utworzyć jakieś statyczne funkcje, które będą operowały na tych obiektach ;)
Po pierwsze: wrapper co replikuje 100% funkcjonalności wrappowanej klasy i nic nie dodaje nie ma najmniejszego sensu, a tylko cię spowalnia.

1.
Hmm... po ogólnym zastanowieniu, doszedłem do wniosku, że nie ma co deklarować zmiennych w klasach, więc mogę utworzyć klasę Manager lub poradzić sobie nawet bez niej. Następnie utworzyć klasę z funkcjami statycznymi(?) lub jakieś funkcje globalne umieszczone w przestrzeni nazw, które z kolei wykonują/manipulują danymi, dopiero tam piszę kod dodający tekstury, tworzący okna itp.
Po pierwsze: jak masz zamiar pisać w C to pisz w C.



Po drugie: jak się nie zna podstaw podstaw to nie bierze się za frameworka od wszystkiego, bo stracisz na nim niebotycznie dużo czasu, w między czasie przyswajając te podstawy, co będzie skutkować wywaleniem całego frameworka przez okno bo będzie tak beznadziejnie napisany.

Offline Xenox93

  • Użytkownik

# Marzec 27, 2014, 16:41:58
Nie upieram się przy Manager'ach. Mogę i bez nich sobie poradzić. Ale bez klas, które operują na danych już nie. Prosty przykład, dodawanie okna różni się od dodania tekstury lub inicjalizacji OGL'a.

1.
Cytuj
Opadły mi ręce jak zobaczyłem ze się upierasz przy tych menedżerach zamiast korzystać z klas std wprost.
Korzystam z kontenerów wprost, tylko klasa, którą tworzę robi coś więcej. Zamiast tylko dodawać nowy element, również korzystam z zewnętrznego API. Tak więc, używam w 100% STL i API. Przykładowa metoda dodania okna:
bool Create( ... )
{
        // Dodanie nowego elementu do kontenera
        // np. windowParams.push_back( WindowParam( ... ) );

        // Rejestrowanie nowej klasy okna ( w zależności od użytego API )

        // Utworzenie okna ( również w zależności od API ) oraz dodanie uchwytu okna do nowo utworzonego
        // elementu, np. vector'a

        return true;
}
Jak widać, sama metoda robi coś więcej niż oferuje STL. Oczywiście, że mógłbym pisać taki kod w funkcji main za każdym razem, ale po to stworzono klasy, funkcje, które wyręczają programistę, a tym samym kod staje się czytelniejszy.

2.
Nie podoba mi się twoje używanie stringów jako identyfikatorów w kodzie... Można wiedzieć, dlaczego? O ile mi wiadomo, bardziej ludzkim sposobem opisywania czegoś, szukania lub tworzenia są nazwy niż indeksy. Jeżeli na scenie będziesz miał 1 mln obiektów, modeli, siatek to wygodnie i intuicyjnie jest podać nazwę obiektu niż indeks. Np. chcesz przesunąć na scenie krzesło, to wygodniej jest napisać : "krzesło_1" niż indeks: 10000, który nie dość, że trzeba zapamiętać to można się jeszcze pogubić lub po usunięciu kilku elementów może się okazać, że od nowa trzeba edytować resztę elementów, które współgrają z tym modelem, np. fizyka, bo ID zmieniło się z 10000 na 9890, co jest kiepskim rozwiązaniem.

Dodatkowo współczesne silniki mają taką opcję, gdy wyszukujemy obiekt z listy na scenie, nie podajemy indeksu a nazwę, np. "Object001".

3.
Jak będziesz potrzebować zadeklarować zmienną to dwa razy się zastanowisz gdzie to zrobić... czy  trzymać na stosie i puścić jako argument do funkcji, czy jakaś funkcja go konstruuje, czy rzeczywiście? Czy rzeczywiście potrzebuje caaały czas tego stanu, czy może wystarczy klasa przechowująca jakiś stan tymczasowo która tylko przechowa pomiędzy wywołaniami.Nie rozumiem za bardzo, co miałeś na myśli. Jednakże biorąc poniższy cytat zrozumiałem go tak ja to przedstawiłem w poniższym kodzie:
a)
Cytuj
[...] czy  trzymać na stosie i puścić jako argument do funkcji, czy jakaś funkcja go konstruuje [...]
vector<StrukturaOpisującaElement> struktura;

// jakaś funkcja globalna, statyczna, która konstruuje zmienną przekazaną przez wartość za pomocą referencji
// pierwszy argument to struktura, która opisuję, np. okno( m.in. rozdzielczość )
// drugi argument to referencja na kontener, który przechowuje kilka elementów, obiektów
void Create( StrukturaOpisującaElement opis, vector<StrukturaOpisującaElement> &str  )
{
        // Coś tu się dzieje
        // np. na początku mogę sprawdzić czy element nie został już dodany
        [...]

        // jeśli nie to zostaje dodany
        str.push_back( StrukturaOpisującaElement( opis ) );

        // Dodatkowo można zrobić coś więcej, np. za pomocą WinAPI zarejestrować klasę okna i utworzyć okno
}

4.
Cytuj
Nie widzę jaaakież to dane miałby przechowywać kontener które nie mogłyby się znaleźć w tej samej klasie gdzie kontener jest używany. Jakieś przykłady gdzie taka enkapsulacja byłaby uzasadniona praktycznymi względami?
Tak więc od samego początku przedstawiłem takie rozwiązanie, które stosowałem dotychczas. Nigdzie nie pisałem, że zamierzam zastąpić kontenery STL, a tak na prawdę chce je użyć. Jednakże po opisach innych zrozumiałem, że jest to hybryda Manager'a oraz wrapper'a / klasy, która coś robi na danych.

5.
Cytuj
Po pierwsze: mylisz klasy z obiektami.
Nie do końca. Pisząc obiekt, mam na myśli zadeklarowaną zmienną klasową. Przecież obiekt wygląda w ten sposób:
Klasa obiekt;
6.
Cytuj
Po pierwsze: metody występują tylko jeden raz w całej binarce, nawet jak masz pierdylion obiektów - bo metoda nie różni się niczym (tak, NICZYM) od zwykłej funkcji, poza tym że ma implicite dodatkowy argument (this).
Hmm, tego nie wiedziałem, że funkcja/metoda występuje tylko raz w binarce. Ale o tym, że metoda i funkcją są tym samym to tak. Ale chyba deklaracja kilku obiektów, rezerwuje miejsce w pamięci dla każdej metody dla każdego obiektu z osobna. Weźmy ten przykład:
class Klasa
{
       void Add( ... ) { ... }
};

Klasa klasa1, klasa2;
Zawsze wydawało mi się że rezerwowana jest pamięć dla klasa1 i klasa2. Czyli że metody klasy, z której zbudowane są obiekty zajmują inne miejsce w pamięci i są traktowane oddzielnie. Dopiero poniższa konstrukcja kodu rezerwuje metody raz i pod tym samym adresem:
class Klasa
{
       STATIC void Add( ... ) { ... }
};

[...]
Ale mogłem się mylić, każdy popełnia błędy. Ale tu nie chodzi głównie o to, a bardziej zaprojektowanie kodu, który łatwo można byłoby rozwijać( dodawać nowe funkcjonalności ) i korzystać z obiektowego programowania.

7.
Cytuj
Po pierwsze: naucz się korzystać z tablic.

Po pierwsze: w co najmniej 99% gier jest jedno okno.
Po pierwsze, nie ma sensu korzystania z tablic, gdy ma się kontenery, które można rozszerzać, aż zabraknie pamięci. Tablice mam w małym palcu.

Po drugie, ja nie piszę framework'a tylko do gier, ale również dla aplikacji różnego typu, np. edytor lub kilka programów, które wymagają kilku okien.

8.
Cytuj
Cytuj
7.Właśnie nie zamierzam zamieniać tych kontenerów, a opakować je w klasy lub utworzyć jakieś statyczne funkcje, które będą operowały na tych obiektach ;)
Po pierwsze: wrapper co replikuje 100% funkcjonalności wrappowanej klasy i nic nie dodaje nie ma najmniejszego sensu, a tylko cię spowalnia.
Źle mnie zrozumiałeś, nie chodziło mi o nadpisywanie lub ignorowanie metod kontenerów, a o coś innego. Przedstawię to w poniższym kodzie:
class Window
{
         private:
            vector< WindowParam > windows;

         public:
            Window() {  }
            Window( const Window &w ) { windows.push_back( WindowParam( w.Get() ) ); }
            Window( WindowParam wp ) { windows.push_back( WindowParam( wp ) ); }
            ~Window() {  }

            bool Create( WindowParam wp )
            {
                    // Dodanie nowego elementu
                    windows.push_back( WindowParam( wp ) );

                     // Za pomocą WinAPI zarejestrować klasę okna, utworzyć okno i ustawić je aktywym, jeśli coś
                     // się nie powiedzie - return false; Dodatkowo dodanie logger'a, który będzie zapisywał do
                     // pliku niepowodzenia

                    return true;
            }

            void Remove( string nazwa_do_usuniecia ) { /* usunięcie tylko jednego okna */ }
            void Clear() { /* usunięcie wszystkich okien */ }

            const WindowParam &Get() { return windows; }
            const WindowParam &Get( string nazwa_okna ) {  /* Zwrócenie okna o danej nazwie */ }
};
To miałem na myśli co przedstawiłem w powyższym kodzie. Nie jest to żaden wrapper na kontener STL. A połączenie dodawania nowego elementu do vector'a wraz z tworzeniem nowego okna oraz sprawdzanie czy się powiodło ;) Metody można ustawić jako statyczne lub w przestrzeni nazw.

a) Owszem klasa wygląda jak hybryda wrapper'a( pewnie nawet nim jest - nieświadomie ), manager'a, zwykłej klasy, która coś robi na danych( np. dodaje coś do kontenera STL oraz tworzy okno za pomocą WinAPI ) oraz framework'a. Więc pisząc o hybrydzie, wrapper'ach, manager'ach itd. itd. miałem na myśli taką konstrukcję.

b) Do tego co chcę napisać, podchodziłbym trochę inaczej. Weźmy przykład hydraulika, który wcześniej się już pojawił.
Otóż, hydraulik nie jest w rurze, ale również nie jest tak, że jeden hydraulik jest przydzielony tylko do jednej rury, nawet jeśli każdy z nich robi to samo - oczyszcza. Np. kupiłeś jeden odcinek rury, to może on zostać obsłużony tylko przez danego hydraulika, a drugiego już nie, pomimo, że obaj robią to samo i wystarczyłby jeden, co jest pozbawione sensu i logiki.

A kod, który ewidentnie ma odzwierciedlenie w rzeczywistości:
Hydraulik hydraulik;
// lub pozbycie się klasy Hydraulik i utworzenie funkcji globalnych w przestrzeni nazw
namespace Hydraulik
{
      void oczysc() {  }
};

vector<Rura> rurociag( liczba_rur ); // Rura to struktura, można dodać ją do Manager'a lub jak każdy z Was
// sugerował zadeklarować ją w pewnym miejscu i dopiero za pomocą funkcji/metody wykonywać na niej operacje

// Dla pierwszej wersji
for( int i = 0; i < liczba_rur; i++ )
       hydraulik.oczysc( rurociag[i] );

// Dla drugiej wersji
for( int i = 0; i < liczba_rur; i++ )
       hydraulik::oczysc( rurociag[i] );

Tak samo u mnie, jest jedna klasa, np. Window, która ma metody tworzące okno( jeszcze raz zaznaczam, że nie wrappuję kontenerów STL, a w jednej metodzie łączę kod dodający element do tegoż kontenera oraz zewnętrznego API ).

Gdybym miał zrobić tak jak piszecie, czyli nie tworzyć klasy Window, to kod w funkcji main wyglądałby strasznie, nawet z użyciem tablic lub samych kontenerów STL:
int main()
{
     vector<Window> windows( liczba_okien );

     for( int i = 0; i < liczba_okien; i++ )
    {
          // Rejestrowanie klasy okna dla każdego okna osobno
          [...]

          // Utworzenie okna
          [...]

          // Ustawienie każdego aktywnym itp.
          [...]
    }

    // Lub wersja hardcore

    // Rejestrowanie klasy okna dla każdego okna osobno
    [...]

    // Utworzenie okna
    [...]

    // Ustawienie każdego aktywnym itp.
    [...]

    // i tak n razy, co nie wygląda ładnie, przejrzyście i intuicyjnie :/
    // Lepiej byłoby zrobić coś takiego, Window to może być przestrzeń nazw( namespace ):
    Window::Create( WindowParams{ 800, 600, FULLSCREEN, ... } );
    Window::Create( WindowParams{ 320, 240, windowed, ... } );
    [...]

     return 0;
}

9.
Cytuj
Po pierwsze: jak masz zamiar pisać w C to pisz w C.
Metody statyczne to nie tylko domena C, a również C++. Co innego printf a cout i endl.

10.
Cytuj
Po drugie: jak się nie zna podstaw podstaw to nie bierze się za frameworka od wszystkiego, bo stracisz na nim niebotycznie dużo czasu, w między czasie przyswajając te podstawy, co będzie skutkować wywaleniem całego frameworka przez okno bo będzie tak beznadziejnie napisany.
Framework pisze każdy z nas, np. jeżeli poprzedni projekt nie wypalił, to kopiujemy kod tworzący okno i kontekst grafiki( OGL/DX ) jeśli jest poprawny. Więc nie trzeba mieć ogromnych zdolności. Przecież Framework jest to kod, który powtarzamy za każdym razem, a nie potrzebnie, np. wcześniej wspomniane okno, kontekst OGL'a i np. ten sam kod, który dodaje shader, teksturę, modele 3D/2D i je renderuje.

Reasumując, czy nie lepiej napisać taką klasę, którą przedstawiłem w tym komentarzu? Czyli klasa, która trzyma w funkcji prywatnej kontener STL, który z kolei trzyma informacje o oknach/elementach, a metody operują na danych i pozwalają na przekazanie obiektu, gdzieś dalej. Bądź utworzenie przestrzeni nazw z funkcjami, które operują na kontenerach STL. Ten pomysł z powyższym niewiele się różni ;)

I nadal proszę Was o to, abyście nie myśleli, że chcę opakowywać kontenery, API itp. lub tworzyć Manager. Gdyż w rzeczywistości chcę w jednej metodzie mieć wszystko co jest wymagane, np. do utworzenia okna, a tym samym mieć obiekty/elementy w jednym miejscu.

Offline lethern

  • Użytkownik

  • +3
# Marzec 27, 2014, 20:23:15
Cytuj
Można wiedzieć, dlaczego? O ile mi wiadomo, bardziej ludzkim sposobem opisywania czegoś, szukania lub tworzenia są nazwy niż indeksy. Jeżeli na scenie będziesz miał 1 mln obiektów, modeli, siatek to wygodnie i intuicyjnie jest podać nazwę obiektu niż indeks. Np. chcesz przesunąć na scenie krzesło, to wygodniej jest napisać : "krzesło_1" niż indeks: 10000

wyobrażasz sobie taki kod?

func_move(){
manager.get("krzesło").move(10,15);
}

Bo ja raczej nie... A co powiesz o tym?

func_move( arg ){
manager.get( arg.target ).move( arg.x, arg.y );
}

Nie użyjesz gołych stringów w logice, bo w Twoim kodzie docelowo te obiekty nie będą istnieć - (chyba że będzie to demo na 5 obiektów); może być tak że obiekty powstaną dopiero w realtime, w skryptach z których je wczytasz, albo jako efekt działań użytkownika - ale to jeszcze nie istnieje w Twoim kodzie jako "krzesło" :) I jest naprawdę duża szansa, że zrobisz tak:
//typedef string IdentyfikatorObiektu;  //mozemy w dowolnej chwili zmienic
typedef int IdentyfikatorObiektu;
func( mouseEvent ev ){
   IdentyfikatorObiektu klikniety_obiekt = SuperManagerWszystkiego.getByPoint( ev.x, ev.y );
   SuperManagerObiektow.przesun_obiekt( klikniety_obiekt, ev.delta_x, ev.delta_y );
}

Cytuj
Zawsze wydawało mi się że rezerwowana jest pamięć dla klasa1 i klasa2. Czyli że metody klasy, z której zbudowane są obiekty zajmują inne miejsce w pamięci
Bardzo upraszczając, czym jest metoda w Twoim .exe? Jest ciągiem jakichś instrukcji typu asembler. Tak samo funkcja main! Ta metoda z klasy stoi tuż obok funkcji main. A programu nie obchodzi czy w ogóle utworzysz zmienną, obiekt, czy 1000 czy tablicę, bo on i tak metodę wstawi tak samo jak wstawia funkcję main. Poza tym, pamięć dzieli się na pamięć danych i pamięć kodu :P

Cytuj
class Window
{
         private:
            vector< WindowParam > windows;
Co to jest?? Obiekt okno który przechowuje okna (nazwa zmiennej)?
Proponuję Ci małe ćwiczenie - napisz cały kod strukturalnie. Serio! Napisz sobie "tworze_okno()", "usuwam_okno()" i tak dalej. Potem wywal wszystkie funkcje których nie używasz, pogrupuj tak żeby było wygodnie, i tak dalej... Potem, pod warunkiem że znasz zasady i cele programowania obiektowego, przełóż to na obiekty, może uda Ci się tu znaleźć nowy punkt widzenia
« Ostatnia zmiana: Marzec 27, 2014, 20:37:35 wysłana przez lethern »

Offline Xirdus

  • Redaktor

  • +1
# Marzec 27, 2014, 22:43:07
2.
Nie podoba mi się twoje używanie stringów jako identyfikatorów w kodzie... Można wiedzieć, dlaczego? O ile mi wiadomo, bardziej ludzkim sposobem opisywania czegoś, szukania lub tworzenia są nazwy niż indeksy.
Z tym że programiści to nie ludzie, i nie używają ludzkich sposobów tylko programistycznych. A programistycznym sposobem jest przydzielanie identyfikatorów liczbowych - czy to automatycznie, czy ręcznie. Jeśli nadajesz ręcznie, to jednak przychodzą ludzkie odruchy, i wypada temu nadać jakąś nazwę - do tego służą stałe. A jak z automatu to zapisujemy go do jakiejś zmiennej (która ma swoją nazwę) powiązanej jakoś z danym obiektem i też nie ma problemu.

Protip: jeśli dobrze zaprojektowałeś swój framework i kod aplikacji z niego korzystający, to wcale nie będziesz musiał nigdzie z tych ID-ków korzystać - zwalczamy problem likwidując przyczynę, a nie skutki.

6.Hmm, tego nie wiedziałem, że funkcja/metoda występuje tylko raz w binarce.
A to tylko jedna z dziesięciu tysięcy rzeczy, które trzeba koniecznie wiedzieć, jeśli chce się choćby zacząć myśleć o wydajności. Z drugiej strony, nie jest ci to do niczego potrzebne, jeśli o optymalizacji w ogóle nie myślisz (co, jak pokazuje sukces Javy*, nie jest aż takim złym podejściem).

7.Po pierwsze, nie ma sensu korzystania z tablic, gdy ma się kontenery, które można rozszerzać, aż zabraknie pamięci. Tablice mam w małym palcu.
Naucz się, w sensie krzeslo[100] zamiast krzeslo_1, krzeslo_2, ..., krzeslo_99. Poza tym, tablice wcale nie są takie nieprzydatne, nawet w świecie STL-a.

Gdybym miał zrobić tak jak piszecie, czyli nie tworzyć klasy Window, to kod w funkcji main wyglądałby strasznie, nawet z użyciem tablic lub samych kontenerów STL:
int main()
{
     vector<Window> windows( liczba_okien );

     for( int i = 0; i < liczba_okien; i++ )
    {
          // Rejestrowanie klasy okna dla każdego okna osobno
          [...]

          // Utworzenie okna
          [...]

          // Ustawienie każdego aktywnym itp.
          [...]
    }

    // Lub wersja hardcore

    // Rejestrowanie klasy okna dla każdego okna osobno
    [...]

    // Utworzenie okna
    [...]

    // Ustawienie każdego aktywnym itp.
    [...]

    // i tak n razy, co nie wygląda ładnie, przejrzyście i intuicyjnie :/
    // Lepiej byłoby zrobić coś takiego, Window to może być przestrzeń nazw( namespace ):
    Window::Create( WindowParams{ 800, 600, FULLSCREEN, ... } );
    Window::Create( WindowParams{ 320, 240, windowed, ... } );
    [...]

     return 0;
}
O ile rozwiązanie drugie jest do bani dla n > 3, o tyle pierwsze (z forem) jest jak najbardziej w porządku - pętla tworząca okna ręcznie jest funkcjonalnie identyczna z pętlą wywołującą funkcję. Oczywiście gdy nie jest to jedyne miejsce gdzie tworzy się okno, funkcja jest dużo, dużo praktyczniejsza i tylko idiota nie wydzieliłby tego - nawet gdy "niejedyne miejsce" oznacza dwa takie miejsca na całą bazę kodu. Jednak osobiście nawet przy jednym takim miejscu wydzieliłbym funkcję - bo tak mi się podoba.

9.Metody statyczne to nie tylko domena C, a również C++. Co innego printf a cout i endl.
Jeśli wszystko jest metodą statyczną lub funkcją globalną, a wszystkie klasy z danymi są PODami bez żadnych metod, to trudno nazwać to C++. Po to wymyślono ukryty this, by nie trzeba było przekazywać wszędzie wskaźników jak w WinAPI. Ale jeśli nie lubisz ukrytej magii (masz do tego prawo - pewien sławny programista hipsterskich systemów operacyjnych też nie lubi), to C++ jest kiepskim wyborem, i czysty C będzie ci o wiele bardziej pasował.

PS większość programistów woli printf() od cout - nawet tych arcy-OOP-owych.

10.Framework pisze każdy z nas, np. jeżeli poprzedni projekt nie wypalił, to kopiujemy kod tworzący okno i kontekst grafiki( OGL/DX ) jeśli jest poprawny. Więc nie trzeba mieć ogromnych zdolności. Przecież Framework jest to kod, który powtarzamy za każdym razem, a nie potrzebnie, np. wcześniej wspomniane okno, kontekst OGL'a i np. ten sam kod, który dodaje shader, teksturę, modele 3D/2D i je renderuje.
Jeśli robisz parę prostych helperów od nudnych rzeczy, które są proste jak budowa cepa, to masz rację. Ale jak robisz cały system zarządzania zasobami, to jak jest źle zaprojektowany to pójdzie do kosza w całości, prędzej czy później.

Reasumując, czy nie lepiej napisać taką klasę, którą przedstawiłem w tym komentarzu? Czyli klasa, która trzyma w funkcji prywatnej kontener STL, który z kolei trzyma informacje o oknach/elementach, a metody operują na danych i pozwalają na przekazanie obiektu, gdzieś dalej. Bądź utworzenie przestrzeni nazw z funkcjami, które operują na kontenerach STL. Ten pomysł z powyższym niewiele się różni ;)
Manie obiektów w jednym miejscu jest jednym z najgorszych antywzorców - bo takie coś najgorzej zmienić. Oczywiście czasem się przydaje, i sam często korzystam z globali - ale tylko i wyłącznie wtedy, kiedy niezbędne jest, by był do czegoś dostęp z dowolnego miejsca w kodzie. Jeśli tak nie jest, o wiele lepszym rozwiązaniem jest przekazywanie w konstruktorach poszczególnych klas referencji do wszystkich obiektów, które potrzebują.

Proponuję Ci małe ćwiczenie - napisz cały kod strukturalnie. Serio! Napisz sobie "tworze_okno()", "usuwam_okno()" i tak dalej. Potem wywal wszystkie funkcje których nie używasz, pogrupuj tak żeby było wygodnie, i tak dalej... Potem, pod warunkiem że znasz zasady i cele programowania obiektowego, przełóż to na obiekty, może uda Ci się tu znaleźć nowy punkt widzenia
+1. Hint: jest to idealna okazja na zapoznanie się z C11 (nie mylić z C++11).

Offline ArekBal

  • Użytkownik

  • +1
# Marzec 28, 2014, 00:52:46
Cytuj
Naucz się, w sensie krzeslo[100] zamiast krzeslo_1, krzeslo_2, ..., krzeslo_99. Poza tym, tablice wcale nie są takie nieprzydatne, nawet w świecie STL-a.
tablice będą lepsze wtedy gdy potrzebujesz iterować po tych krzesłach. Dla każdego innego przypadku te przecinki będą lepsze.

Co do tych stringów to koledzy dobrze to opisali...
porównanie stringów to strcmp... często znak po znaczku.
Porównywanie char* (literały tylko, ale nie zawsze) ma za to bardzo ograniczone zastosowanie.

stringi są ok tylko jako nazwy obiektów na potrzeby edytora... czy wyszukiwanie obiektów po serializacji...

A używanie literałów w kodzie bardzo źle świadczy o każdym kto to robi. A w twoich przykładach robisz to nagminnie.

PS: Nie rejestrujesz klasy okna dla każdego okna osobno... To raczej tam popracuj nad architekturą...

Xirdus: Można pisać w c++ strukturalnie... nie musisz od razu mu nakazywać używać "this".
Zamiast tworzyć wrapper na mapę czy vectora i wystawić jedną metodę lepiej jest tą metodę wystawić jako statyczną funkcję przyjmującą jako pierwszy argument tą kolekcje. Jest to praktyczniejsze...

Offline Xender

  • Użytkownik

# Marzec 28, 2014, 01:39:08
Xirdus: Można pisać w c++ strukturalnie... nie musisz od razu mu nakazywać używać "this".
Zamiast tworzyć wrapper na mapę czy vectora i wystawić jedną metodę lepiej jest tą metodę wystawić jako statyczną funkcję przyjmującą jako pierwszy argument tą kolekcje. Jest to praktyczniejsze...
To nadal jest programowanie obiektowe. O obiektowości świadczy semantyka, nie składnia, dlatego tak samo można pisać obiektowo w C.
Zmiana słowa class na struct i składni foo.bar(a, b, c); na bar(&foo, a, b, c); to tylko zmiany na poziomie składniowym - znaczą nadal to samo.
Dlatego na to, co nazywasz programowaniem "strukturalnym", lepszym określeniem będzie właśnie "obiektowe w stylu C".

Jest to o tyle ważne, iż prawdziwe "programowanie strukturalne" to zupełnie inny paradygmat, na całkiem innym (bliższym kodu) poziomie abstrakcji - https://pl.wikipedia.org/wiki/Programowanie_strukturalne. Programowanie strukturalne jest całkowicie ortogonalne (niezależne) do obiektowego. Tak naprawdę ta nazwa to potworek, powinno to się nazywać "programowanie ustrukturyzowane"("structured programming"), ponieważ chodzi tu o strukturę kodu, a nie danych.