![]() |
![]() |
![]()
Post
#1
|
|
Grupa: Zarejestrowani Postów: 34 Pomógł: 3 Dołączył: 29.05.2011 Ostrzeżenie: (0%) ![]() ![]() |
Witam!
Niedawno rozpocząłem pisanie sobie zbioru przydatnych klas i funkcji, które roboczo nazywam moim mini 'frameworkiem'. Trafiła tam już m. in. klasa Uploader'a, którą pomogliście mi dopracować. Stwierdziłem, że przy wielu projektach przydatna stanie się klasa wpisów inaczej newsów. Nowości występują w większości stron internetowych. Przechodząc do sedna sprawy: Myśląc abstrakcyjnie news posiada swoje prywatne pola jakimi są tytuł, treść, data itp. Jednak brakowało mi możliwości zarządzania wpisami tak bardziej 'z góry'. Wymyśliłem klasę zarządzającą wpisami CNewsManager. Cała konstrukcja wydaje mi się o tyle korzystna (także pod względem wydajności), że do pobrania newsów wystarczy jedno zapytanie do bazy danych. Przejdźmy do kodu. Klasa CNewsManager:
Klasa CNews:
Tutaj pojawia się moje pytanie: czy koniecznością jest przekazywanie takiej ilości parametrów do konstruktora CNews? Chciałbym także poznać wasze opinie co do budowy i mojego sposobu myślenia w tym przypadku. Chętnie dowiem się jakie rozwiązania proponujecie oraz jakie porady macie dla mnie co do pisania kodu. Teraz skrawek kodu przykładowego:
Pozdrawiam Jakieś propozycje? PS. Nie wiem czy temat umieściłem w odpowiednim dziale, w razie problemów proszę moderatora o przeniesienie. Już niedługo dodam także klasę komentarzy. Pozdrawiam |
|
|
![]() |
![]()
Post
#2
|
|
Grupa: Moderatorzy Postów: 4 362 Pomógł: 714 Dołączył: 12.02.2009 Skąd: Jak się położę tak leżę :D ![]() |
Powiem tak... Niemal większość rzeczy w Twoim managerze powinna być implementowana przez CNews (IMG:style_emoticons/default/wink.gif)
CreateNews - to tylko opakowanie tworzenia newsa, na dodatek extendedContent powinna sama klasa CNews wyłapać, a nie, że różne konstruktory wywołujesz. Konstruktor i tak sprawdza czy tam jest jakaś wartość, więc nawet jeśli wywołasz ją w klasie nadzorczej to i tak konstruktor będzie sprawdzał, czy ona jest. Bo tak konstruktor napisałeś. Load - za ładowanie całej tabeli w przypadku braku początku i końca, to za jaja by Cię trzeba było powiesić (IMG:style_emoticons/default/wink.gif) W takiej sytuacji powinieneś ładować ostatnich X newsów. To co chcesz robić to tylko na desktopie, gdzie masz taki obiekt cały czas w pamięci, a nie, że tworzony za każdym razem od nowa z każdym żądaniem! To wstęp do paginacji. Inna sprawa, że powinieneś się tam zastanowić nad sensem przeciążenia metody load() lub stworzenie kilku wariantywnych: loadSingle - ładuje jeden wybrany, loadRange - ładuje zakres od-do po id lub kolejności w bazie danych - Twój wybór, loadDefault - ładuje X ostatnich, loadChosen - ładuje wybrane przez użytkownika. ClearNewsBuffer - po co pętla? $this->$aNewsBuffer = array(); i pozamiatane SelectNews - pisałem wyżej o load... ShowAll - znowu extendedContent rozróżniane jak w CreateNews, grrrrr... Poza tym "dziedziczy" ona błąd klasy CNews... Mieszanie modelu z widokiem. Od kiedy model na pałę wartości wali w kod HTML? A to robi CNews->show() Poza tym gratuluję pomysłu "chainingu"... SelectNews( nieistniejąceId )-> show(); Jak zrobisz show() na wartości false? Ja już nawet nie ruszam tematu: "Chłopaki... Nie możemy użyć RDBMS. Musimy przejść na pliki XML jako formę przechowania danych." i jedno wielkie "K..wa mać! Framework tego nie przewiduje!" |
|
|
![]()
Post
#3
|
|
Grupa: Zarejestrowani Postów: 34 Pomógł: 3 Dołączył: 29.05.2011 Ostrzeżenie: (0%) ![]() ![]() |
Wielkie dzięki za rozjaśnienie kilku istotnych rzeczy.
Zgodzę się, że: -CreateNews - fakt, zbytnio obudowałem to klasą nadzorującą, lecz czy po usunięciu wywołania dwóch konstruktorów takie obudowanie jest niewłaściwe? - Load - kilka przeładowań tej funkcji. Wolę uniknąć wieszania za jaja :-), - ClearNewsBuffer - no tak, jakieś przyzwyczajenie po destruktorze w C++. Jeżeli chodzi o resztę mam kilka pytań. Powiedzmy, że załadowałem $NewsManager->loadDefault(x), x ostatnich wpisów i chcę operować na jednym (załadowanym już w bufforze, aby nie operować na zapytaniach). Myślałem właśnie nad utworzeniem funkcji pokroju $NewsManager->selectNews(), lecz w przypadku błędnego ID wszystko się sypie. Spodziewałem się wytknięcia błędu jak to nazwałeś 'mieszania modelu z widokiem', lecz zupełnie nie mam pojęcia o co w tym chodzi. W sensie, że klasa nie powinna mieć metody 'renderującej'? Jeszcze raz dzięki za w ogóle pofatygowanie się w czytaniu moich wypocin. Pozdrawiam EDIT: Czyli funkcja loadCokolwiek, powinna zwracać obiekt CNews w przypadku jednego wyniku lub tablicę obiektów jeżeli rezultat jest większy od jednego? Ten post edytował Sagnitor 26.06.2011, 16:11:52 |
|
|
![]()
Post
#4
|
|
Grupa: Moderatorzy Postów: 4 362 Pomógł: 714 Dołączył: 12.02.2009 Skąd: Jak się położę tak leżę :D ![]() |
Zależy jak spojrzeć z tym obudowaniem... Ja bym czegoś takiego jak dodawanie i edycja czy usunięcie pojedynczego elementu nawet nie dodawał do klasy nadzorującej. Nadzorca robi to hurtowo, a więc edytujesz, usuwasz i dodajesz elementy kolekcji od razu jako wielokrotności. Jeśli miałbym coś w takim nadzorcy dodać to jedynie odwołania do klasy podlegającej. W środowisku Web byłoby to odwołanie się do kontrolera danej klasy. Innymi słowy gdybym chciał dodać nowy obiekt, to nie z poziomu zarządcy bym go tworzył, ale zwrócił się bezpośrednio do samej klasy podrzędnej. O co mi chodzi? Skoro masz klasę CNews, to na bank ma ona swój formularz dodawania i edycji. I to do niego bym kierował, a nie jakiejś sztucznej w nadzorcy, która robi dokładnie to samo. Innymi słowy przekierowanie na metodę create klasy CNews i jej własny widok (IMG:style_emoticons/default/smile.gif)
Jeśli chcesz select, to czemu w tym momencie zamiast id nie użyjesz offsetu lub indeksu tablicowego? I tak używasz tablicy... Jaka z tego korzyść? Próba odwołania do nie istniejącego elementu mogła by być sygnalizowana wyjątkiem. Klasa nie tyle nie powinna mieć metody renderującej co powinna mieć własny widok lub kilka widoków i to do nich się odwoływać. Sam powiedz... Jeśli zrobiłbym taki render jak Ty walnąłeś, to skąd miałbym pewność, że dopasuje mi się to do całej strony? Walnąłeś bowiem wszystko w tr->td a skąd mam mieć pewność, że ów widok trafi do strony, która mi to opakuje potem w tablicę? Wszystkie funkcje load powinny zwracać tablicę. Choćby z jednym tylko elementem. No chyba, że loadSingle mógłby zwrócić obiekt klasy CNews i wtedy ładnie byś miał od razu z mostu dostęp do zmiennej this. Ale z racji zgodności z innymi wariantami obstawałbym za tablicą i tam odwołaniem do iteratora i/lub metody current(). No i w razie tablicy mógłbym przewijać się po elementach metodami prev(), next(), current() |
|
|
![]()
Post
#5
|
|
Grupa: Zarejestrowani Postów: 34 Pomógł: 3 Dołączył: 29.05.2011 Ostrzeżenie: (0%) ![]() ![]() |
Teraz zrozumiałem bezsensowność zapisywania w tablicy pod indeksem równym ID. Dzięki jeszcze raz za rady.
Niedługo może wrzucę tu zmodyfikowaną i poprawioną wersję tej pełnej nieporozumień klasy (IMG:style_emoticons/default/wink.gif) . Na początku tworząc w ogóle obiektowy system wpisów, stworzyłem samą klasę CNews w troszkę innej formie. Brakowało mi właśnie takiego 'nadzorcy', który robiłby to hurtowo, jednak w trakcie pisania CNewsManager widocznie zapomniałem o jego przeznaczeniu. Z metod load, będę zwracać tablice z danymi newsów, bo po co wywoływać X konstruktorów i tworzyć tablicę obiektów, skoro można powybierać. Zastanawiam się jeszcze nad metodą loadChosen(). Sednem sprawy są parametry (nie wiadomo ile użytkownik ich przekaże). Pogrzebię u wujka Google na ten temat. Tak, czy siak kliknąłem zasłużone oraz symboliczne "Pomógł". Pozdrawiam Sagni |
|
|
![]()
Post
#6
|
|
Grupa: Moderatorzy Postów: 4 362 Pomógł: 714 Dołączył: 12.02.2009 Skąd: Jak się położę tak leżę :D ![]() |
EDIT:
Cytat ClearNewsBuffer - no tak, jakieś przyzwyczajenie po destruktorze w C++ Miałem to samo (IMG:style_emoticons/default/wink.gif) Na szczęście szybko przeszedłem nad tym do porządku dziennego. Inna sprawa, że robiąc tak jak Ty proponowałeś, doprowadziłbyś do powstania tablicy NULLi, co też nie jest prawidłowe.Jeszcze odniosę się do propozycji: klasa CNews: metody: konstruktor, add, update, delete i settery oraz gettery dla poszczególnych pól Większość tego już masz, ale nie zawsze prawidłowo. To czego Ci brakuje to brak obiektu walidacji, kontroli uprawnień i warstwy abstrakcji na zapis. Kompletnie olałeś te aspekty. Operacja na danych powinna być przeniesiona do jakiegoś abstrakcyjnego bytu, który dopiero decydowałby czy operacje działają jako plikowe czy na bazie danych. Gdyby tak przyjąć, to operacje add i update magła by być jedną metodą save(), która obiekt klasy CNews przekazała by wyżej gdzieś do warstwy modelu. On by się już sam tym zajął. I to w owej warstwie abstrakcji byś decydował na czym tak naprawdę pracujesz. Ów obiekt abstrakcyjny bym wstrzykiwał do klas z danymi pracującymi. Zobacz może jakiś ORM by mniej więcej załapać o co w tym mniej więcej chodzi. Przypuśćmy klasa Storage, która działa wedle wzorca Strategia nie byłaby tu głupim pomysłem. Używałbyś zawsze tego samego obiektu, ale implementacja konkretnego silnika bazodanowego ( nawet poprzez kolejna abstrakcję, jaką jest PDO ) lub operacji plikowych byłaby dla klienta niewidoczna. Zależy co chcesz osiągnąć... Czasem tablica obiektów mogła by być lepsza. Czemu? Przypuśćmy takie wywołanie
Zauważ ciekawą rzecz... Iteruję metodami next po tablicy obiektów i gdy chcę konkretny daję current, co zwraca mi wprost obiekt klasy CNews, a więc mogę użyć jego własnego gettera getTitle. Mogłem użyć też przypuśćmy zamiast dwóch next() własnej metody index(2), która w przypadku niepowodzenia sypnęła by mi wyjątkiem. Tak samo obwarowałbym wyjątkami ogólnie iterację po tablicy wyników. |
|
|
![]()
Post
#7
|
|
Grupa: Zarejestrowani Postów: 34 Pomógł: 3 Dołączył: 29.05.2011 Ostrzeżenie: (0%) ![]() ![]() |
Cytat To czego Ci brakuje to brak obiektu walidacji, kontroli uprawnień i warstwy abstrakcji na zapis. Prostą przyczyną braku tych aspektów jest brak wiedzy, jak się za to zabrać i z czym to się je. (IMG:style_emoticons/default/wink.gif) Nigdy nie projektowałem jakiś większych frameworków, także wykładam się w tym miejscu brakiem doświadczenia. Widzę, że projektowanie relacji pomiędzy klasami szybko może się przeistoczyć w coś większego. Jak na razie po kilku zmianach (m. in. utworzeniu metody save(), zamiast add() i upload() ), wyciąganie danych wygląda w ten sposób:
Zmieniłem także dodawanie parametrów w konstruktorze CNews (tytuł, zawartość itp) na opcjonalne, w razie tworzenia nowego newsa wykorzysta się $News->save($title, $content itp). EDIT: Masz może jakieś tutoriale lub książki traktujące o takiej warstwowości? Pozdrawiam Ten post edytował Sagnitor 27.06.2011, 09:45:38 |
|
|
![]()
Post
#8
|
|
Grupa: Moderatorzy Postów: 4 362 Pomógł: 714 Dołączył: 12.02.2009 Skąd: Jak się położę tak leżę :D ![]() |
Osobiście nie spotkałem jakichś podręczników do tego. To kwestia dużej ilości czytania, między innymi na temat wzorców, oraz grzebania wewnątrz kodu frameworków, czyli raczej coś, co dla początkujących osob jest mało przyjemne. Myślę, ze na początek powinieneś poczytac o samym wzorcu Strategia (a także innych podstawowych), by zrozumieć o co w nim chodzi. Wtedy złapiesz jak to mniej więcej można zaimplementować. Gdybyś to zaimplementował, to zapewne wyszedłbyś z połaczeniami do jakiegoś pliku konfiguracyjnego, gdzie byś raz a porządnie określał wszystko o formie (file, mysql, pdo czy xml) źródła danych i potem FW sam by się tego trzymał.
Co do uprawnień to poczytaj choćby tutaj wpisy i artykuły o ACL oraz rozwiń to. A walidacja... Nieco napomknięto w artykule o refaktoryzacji na wortalu. Ogólnie jednak lepiej podejrzeć jak to robią większe FW. No cóż... Niestety w programowaniu wiele rzeczy nie jest ładnie opisanych i zrozumiale. Najwięcej człowiek uczy się przegladając kod dużych projektów, a to nie jest dla wielu ani łatwe, ani przyjemne. Można poczytać książki, można znaleźć trochę tutoriali, ale one tylko najprostsze rzeczy przedstawią i może ciut ostrzegą przed wpadkami poważnymi, ale porządnej implementacji tam raczej nie uświadczysz. |
|
|
![]()
Post
#9
|
|
Grupa: Zarejestrowani Postów: 34 Pomógł: 3 Dołączył: 29.05.2011 Ostrzeżenie: (0%) ![]() ![]() |
Czytając teraz troszkę o Strategii, zrozumiałem, że gdybym zrobił Interface INews o metodzie save();, to potem tworząc dwie klasy implementujące ten interface, w różny sposób wykonywana by była ta czynność. Z tego co zrozumiałem sugerujesz dodać klasę/warstwę abstrakcji zapisu, która wczytywałaby z pliku config, w jaki sposób dokonywać zapisu wpisów oraz innych czynności. Dobrze rozumuję? (IMG:style_emoticons/default/wink.gif)
|
|
|
![]()
Post
#10
|
|
Grupa: Moderatorzy Postów: 4 362 Pomógł: 714 Dołączył: 12.02.2009 Skąd: Jak się położę tak leżę :D ![]() |
No widzisz... Czytanie się opłaca (IMG:style_emoticons/default/smile.gif) Co do konfiga, to myślałem o tym, by podczas żądań strona sobie z konfiga czytała czego używa i wybierała odpowiednią implementację obsługi źródła danych. Dla całości serwisu miałbyś bowiem dostępne metody zdefiniowane w interfejsie i tylko ich byś używał. To JAK by to było od strony silnika rozwiązane byłoby już problemem samej klasy. Dla Twojego kodu istniały by tylko wywołanie obiektu klasy Storage i jej metod. To jednak jaka klasa by była w ramach Storage odpowiedzialna za reakcję w danej chwili, działoby się "pod maską". Mógłbyś też poczytać o wzorcu Factory (metoda wytwórcza), Abstract Factory i Builder, bo je też można wykorzystać i nawet sądzę, że któraś z nich będzie lepsza w Twoim wypadku. Abstract Factory daje Ci transparentność kosztem jednak skomplikowania (masz jeden interfejs od strony klienta, ale w środku możesz mieć nawalone od groma zależności (IMG:style_emoticons/default/wink.gif) ), Factory daje większą swobodę ( duża rozszerzalność i niezależność ), a Builder skupia sie bardziej na utworzeniu obiektu, ale dzięki temu mogą to być bardzo skomplikowane i zróżnicowane twory, a sam wzorzec łatwo się skaluje, ponieważ istnieje w nim pewien nadzorca kierujący całym procesem tworzenia. Dzięki temu całość jest super przenośna, nawet na środowiska wieloprocesorowe czy klastry. Poczytaj trochę o wszystkich i oceń co u Ciebie najbardziej się będzie liczyć oraz wybierz właściwe rozwiązanie.
|
|
|
![]() ![]() |
![]() |
Aktualny czas: 4.10.2025 - 00:11 |