Podczas dzisiejszej prezentacji na temat Software Craftsmanship i Wzorców Projektowych, którą przeprowadziłem w ramach lubelskiego JUG padło szereg pytań odnośnie:
- kosztu posługiwania się w kodzie wzorcami
- presji czasu
- komplikacji
Jest to pewien ogólny i często powtarzający się zestaw pytań, dlatego sądzę, że warto rozwiać kilka wątpliwości i nieporozumień.
Część programistów widzi wykorzystanie wzorca jako niepotrzebną komplikację, która wprowadza dodatkowy narzut na wytworzenie kodu.
Generalnie jeżeli chodzi o poziom komplikacji to wzorce powinny redukować złożoność przypadkową oraz zwiększać czytelność złożoności właściwej. Jeżeli mamy sytuację odwrotną to wzorzec został wykorzystany niepoprawnie lub niepotrzebnie.
Odnośnie narzutu czasowego to obserwuję dziwne przeświadczenie jakoby dopisanie interfejsu czy kilku linijek kodu potrzebnych na podzielenie kodu na większą ilość pudełek (klas) było jakoś niezwykle czasochłonne;) Sam proces myślowy również nie powinien się znacząco wydłużyć. Albo wiem jaki wzorzec użyć albo nie wiem. To nie jest tak, że zastanawiam się nad tym godzinami.
Warto zwrócić uwagę na fakt, że katalog Wzorców Projektowych Ogólnego Przeznaczenia jest dosyć bogaty.
Owszem, część z nich jest dosyć wyrafinowana pod względem koncepcyjnym lub pod względem konstrukcji kodu. Przykładowo taki Visitor, Component Tree, Bridge, Proxy, Builder, Flyweight, Memento prowadzają pewien narzut, ale są to narzędzia, które wybieramy do misji specjalnych. Na pewno nie należą do codziennego wachlarza technik.
Natomiast Strategia, Stan, Dekorator, Template Method, Command to lekkie "skróty myślowe" wynikające wprost w paradygmatu Object Oriented. Nie ma w nich doprawdy nic nadzwyczajnego.
Osobiście traktuję część z nich jako "atomowe klocki", z których powinno budować się rozwiązania. Są to byty niemal tej samej klasy ciężaru co IFy, pętele czy metody. Biegłe opanowanie lekkich wzorców sprawia, że poszerzają one nasz zestaw podstawowych "klocków myślowych". Budujemy z nich najpierw mentalne modele, a później naturalnie pojawiają się w kodzie.
Operując lekkimi wzorcami na poziomie podstawowych klocków nie mamy narzutu czasowego podczas developmentu. Natomiast poziom komplikacji ulega znacznemu obniżeniu, ale tylko dla tych czytelników kodu, który posiadają taki sam zasób skojarzeń jak jego twórca - czyli operują tym samym słownictwem branżowym.
//=====================================
Potrzeba posiłkowania się wzorcami "lekkiego" kalibru wynika ze słabej siły wyrazu języków wywodzących się z C++ takich jak np Java. Przykładowo brak domknięć kompensujemy sobie strategiami. Doprawdy nie jest niczym strasznym i pracochłonnym opracowanie interfejsu z jedną metodą oraz kilku jej implementacji. Ilość kodu "biznesowego" nie ulega zwiększeniu (ew redukcji) natomiast pojawiają się jedynie dodatkowe "opakowania" dla niego w postaci zgrabnych i eleganckich klas.
Jeżeli ktoś aż tak bardzo nie pała chęcią do pisania kodu to może powinien zostać trubadurem;)
Ciężki kaliber wzorców również może czasem przydać się do emulacji brakujących elementów składni, takich jak:
- double dispatch emulowany przez wzorzec Visitor
- lazy evaluation emulowany przez wzorzec Proxy.
Inżynieria oprogramowania w ujęciu systemowym.
Zintegrowane podejście do metodyk,
technologii (głównie Java EE), architektury i rozwoju ścieżki kariery programisty.
wtorek, 23 marca 2010
wtorek, 9 marca 2010
Wyginanie Javy
Dzisiejsza prezentacja Wojtka Gomoła o wyrażeniach Lambda (C# i "emulacja" w Javie) na Lubelskim JUG przypomniała mi o kilku ciekawych zabawkach, które jakiś czas temu znalazłem i zapomniałem o nich napisać.
Ciekawą cechą dynamicznych języków funkcyjnych jest:
- możliwość tworzenia konstrukcji zbliżonych do języka naturalnego
- możliwość rozszerzania standardowych, często używanych klas (zwykle kolekcji, kontenerów) o wygodne metody
- nazwijmy to w sumie roboczo "techniczny DSL"
Przykładowo takie oto sprawiające radość uproszczenia:
kolekcja.wyrzucZNullemWPolu("name").posortuj({e1.age >e2.age}).wytnijPierwszych(10);
Jak dla mnie nie ma się czym ekscytować ponieważ jest to jedynie lukier składniowy i w statycznych językach z silną kontrolą typów jest to tylko kwestia stworzenia jakiegoś starego, dobrego, proceduralnego Utilsa z interfejsem strategii komparacji:
MyAlmightyUtils28.wyrzucZNullemWPolu(kolekcja, "name")
oraz odpowiednia sekwencja wywołań.
/*Oczywiście wszyscy wiemy, że Utilsy są złe i brzydkie, ale co zrobić*/
No ale już nie będzie tak pięknie jak we wcześniejszym przykładzie z jakiegoś sexi-skryptowego-języka.
Na szczęście istnieje kilka ciekawych zabawek zaimplementowanych w Javie. Dzięki cwanym trikom na generykach i zastosowaniu odpowiedniego nazewnictwa dają nam coś więcej niż namiastkę konstrukcji znanych z dynamicznych, słabo otypowanych języków.
op4j - genialna biblioteka, która sprawia, że żmudny kod staje się bardziej czytelny i zwięzły jednocześnie zachowując typowanie.
Bardzo podstawowy przykład:
String[] emailArray = Op.on(userEmails).toArrayOf(Types.STRING).forEach().exec(FnString.toLowerCase()).get();
lambdaj - podobna zabawka ze stajni google. Można rzucić okiem również na google collections.
Mockito - nasza rodzima produkcja. Kto jeszcze nie używał, ten powinien spróbować. Warto zajrzeć choćby do kodu ponieważ stanowi on przykład wzorcowej dokumentacji. Cały manual wraz z przykładami znajduje się w JavaDocu głównej "klaski"! Wzór do naśladowania dla innych twórców bibliotek - bo na pewno nie dla developerów komercyjnych produktów;)
//==================
Czekamy na Javę
No i na pewno na certyfikatach pojawią się kolejne arcyistotne pytania mające na celu sprawdzenie czy delikwent jest w stanie odróżnić: domknięcie od klasy wewnętrznej i od niekompilującego się kodu;P
niedziela, 7 lutego 2010
Inżynieria w służbie użyteczności
/*
*Tytuł niczym wzięty ze sztandaru
*z jakiegoś pochodu
*z uprzedniej smutnej epoki:)
*/
Witam po długiej przerwie. Zaprezentuję dziś pewne ujęcie Usability oraz koncepcję technicznego rozwiązania użytecznej aplikacji - jej architekturę i dwa główne wzorce projektowe.
NOWICJUSZ vs EKSPERT
Użyteczność niejedno ma oblicze. Możemy mówić o przejrzystości, intuicyjności, ergonomii itd. Każdy z nas zapewne zdefiniuje dobre GUI jako takie, które oferuje to czego się spodziewamy. Ale co to znaczy? Oczywiście dla każdego coś innego.
Dziś skupimy się na problemie różnych potrzeb i oczekiwań użytkowników o różnym poziomie zaawansowania.
Analizując Model Rozwoju Kompetencji Braci Dreyfus
krótki opis
szerszy opis
możemy wywnioskować, że użytkownicy reprezentujący skrajne poziomy kompetencji potrzebują skrajnie odmiennego GUI.
Nowicjusz i Zaawansowany Początkujący są nastawieni na zadania ponieważ nie ogarniają jeszcze całości problemu. Potrzebują określonej ścieżki, przez którą będą prowadzeni za rękę a na każdym etapie dostaną jasne wytyczne odnośnie tego CO trzeba zrobić.
Z drugiej zaś strony Kompetentny, Biegły i Ekspert są nastawieni na osiągnięcie celu i mniej lub bardziej orientują się jak go osiągnąć.
Jak to w życiu bywa: "różni ludzie, różne potrzeby".
Mniej zaawansowani użytkownicy potrzebują aplikacji-asystenta zorientowanej na zadania. Aplikacja taka nie powinna atakować mnogością możliwości. Powinna w danym momencie sugerować (lub wręcz zmuszać) do zrobienia właściwej rzeczy. Przykładem mogą być nasze ulubione kretynatory (yyy kreatory).
Zaawansowany użytkownik potrzebuje aplikacji zorientowanej na możliwości. Dla niego wygodne będzie rozbudowane menu, bez sugerowania co kiedy kliknąć. Zaawansowany użytkownik dobrze wie co chce zrobić i nie potrzebuje aby mu podpowiadać. Mało tego, taki użytkownik ma wyrobiony swój własny styl pracy, zatem aplikacja-asystent jedynie przeszkadza. Najlepszym przykładem może być: IDE (np. Eclipse), zaawansowany edytor tekstu albo narzędzie typu CAD. Tysiące opcji w menu i biała kartka na dzień dobry.
Klasyczny problem występujący w aplikacjach biznesowych to nauka obsługi użytkowników. Stare systemy tej klasy orientowało się na możliwości (teraz powoli sytuacja ulega zmianie). Mamy zatem nieszczęsną użytkowniczkę, która zaczyna naukę obsługi systemu (może również jednocześnie "uczy się komputera"). Przytłoczenie mnogością opcji skutkuje jednym: tresurą. Użytkownik nie ma innego wyjścia jak opanowanie procesu biznesowego na zasadzie szympansa: klikam trzeci z lewej, później ok, później przedostatnia zakładka, później klikam drugi od dołu...
Wszyscy znamy też nietrafione rozwiązanie, które miało pomagać nowicjuszom w okiełzaniu eksperckiego narzędzia - spinacz w Office;)
PRZYKŁAD APLIKACJI
W przypadku aplikacji korporacyjnych jest łatwiej. Użytkownik nie ma innego wyjścia jak zacisnąć zęby i nauczyć się (pardon - wytresować się). Jest to po prostu przykre narzędzie jego pracy.
Natomiast gdy nasza aplikacja ma konkurować na wolnym rynku wówczas musimy nieco się wysilić. Użytkownik zirytowany autystycznym GUI aplikacji po prostu porzuci ją, zaklnie i pójdzie sobie do konkurencji:)
Użytkownicy są teraz niezbyt cierpliwi i mają wysokie wymagania odnośnie przyjemności - pokolenie MTV?
Aby ustalić kontekst aplikacji walczącej o klienta wysokim poziomem użyteczności wyobraźmy sobie system do projektowania wnętrz, np. wnętrz kuchni.
Niech zaprojektowanie wymaga między innymi: określenia obszaru pomieszczenia, ustalenia otworów i innych przeszkód, rozstawienia mebli i AGD, dopasowania wszystkich wymiarów, wyboru materiałów z których mają być wykonanie poszczególne elementy, itd. Dalej mamy rysunki techniczne, wyliczenia należności, umowy, itp.
Ustalmy wyzwanie: z aplikacji ma być wykorzystywana przez:
- klientów końcowych, którzy to raz na kilka(naście) lat kupują sobie "kuchnię"
- projektantów kuchni, którzy dziennie wyklikują po kilka takich zestawów.
Co z kompetencjami naszych użytkowników?
Jasne jest, że klient końcowy jest totalnym Nowicjuszem jeżeli chodzi o aplikację. Użyje jej raz (ew jako rodzinny "haker" pomoże szwagrowi wyklikać jego zestaw). Zatem nigdy nie nabędzie wyższych kompetencji. Co gorsza klient może być również niekompetentny w domenie problemu, czyli wszystkich zagadnieniach związanych z kuchniami.
Manual oczywiście odpada, bo zirytowany klient pójdzie sobie do konkurencji.
Projektant jest zwykle w miarę kompetentny w domenie problemu;) Będzie też Nowicjuszem jako użytkownik, ale ponieważ będzie miał motywację i częstą styczność z aplikacją to szybko osiągnie poziom Kompetentny a z czasem dojdzie do Eksperta.
ARCHITEKTURA ROZWIĄZANIA
Mamy zatem dwie klasy użytkowników, którzy potrzebują diametralnie różnego interfejsu. Początkujący użytkownik wymaga przeprowadzenia go przez cały proces projektowania krok po kroku, bez zbędnych pytań, defalutowo, szybko, gładko i bez irytacji. Zaawansowany użytkownik potrzebuje natomiast wolności, możliwości i tysięcy szczegółowych opcji.
Jak to pogodzić? Czy musimy tworzyć niemal 2 osobne aplikacje?
Niekoniecznie, jeżeli podejdziemy do problemu racjonalnie to możemy zredukować narzut związany z przygotowaniem 2 wersji.
Możemy podejść do problemu następująco:

Wydzielamy CORE aplikacji, który zawiera klasy odpowiedzialne za główną funkcjonalność, czyli projektowanie wnętrz (wraz ze wszystkimi regułami z tym związanymi) oraz wizualizację projektu.
Core jest dostępny przez API.
Mamy 2 zestawy GUI: jeden prosty i intuicyjny oraz zorientowany na zadania przeznaczony dla początkujących. Drugi to klasyczny potwór dla eksperta oferujący niezliczone możliwości w rozbudowanych menu.
Oba rodzaje GUI komunikują cię z Corem wysyłając polecenia zhermetyzowane przy użyciu wzorca projektowego Command. Idea wzorca jest taka, że czynności nie są metodami (jak w klasycznym podejściu proceduralnym). Czynności są obiektami. Dla wszystkich klas poleceń istnieje wspólny interfejs zawierający jedną metodę - jest to sygnał do polecenia aby wykonało ono zawartą w sobie logikę.
Konkretny command zawiera w sobie zestaw instrukcji operujących na API Cora. Oczywiście commandy wysyłane z GUI Nowicjusza będą zawierać więcej kodu operującego na API. Jedna czynność takiego użytkownika wykonuje więcej operacji. Commandy wysyłane z GUI Eksperta będą bardziej elementarne. Commandy mogą po sobie dziedziczyć, mogą się na wzajem dekorować z wykorzystaniem wzorca Dekratora w celu reużycia logiki.
W przedstawionej architekturze mamy dodatkowo wzorzec Mediatora. Został on wprowadzony jako "środowisko uruchomieniowe" dla poleceń oraz "hub" spinający wiele komponentów. GUI wysyła commadny do Mediatora, a to mediator wykonuje otrzymane polecenie (uruchamiając wspólną metodę interfejsu) przekazując do nich kontekst uruchomienia, czyli core.
Mediator może również przy okazji manipulować stanem GUI, które nie należy do Core (np nakładać przeźroczystą chmurę na czas długotrwałych operacji bądź włączać dostępność opcji w menu). Mediator może też generować zdarzenia aplikacyjne, które będą obsługiwane przez pewne komponenty graficzne prezentujące zajście danego zdarzenia. Można bawić się do woli z Mediatorem, ale trzeba uważać aby nie stał się boską klasą o zbyt dużej odpowiedzialności.
ZWIĘKSZAMY USABILITY
Każdy użyteczny system, którego idea zasadzą się na bogatej interakcji użytkownika powinien oferować funkcjonalność "Cofnij". Wprowadzenie tej funkcjonalności jest generalnie bardzo kłopotliwe. Kto zmagał się z tym problemem ten zapewne próbował niewydajnego rozwiązania polegającego na "zrzucie" aktualnego stanu do pamięci podczas wykonywania każdej operacji modyfikującej stan.
Ogólnie jest to problematyczna funkcjonalność, chyba że...

...posiłkujemy się wzorcem Command:)
Możemy wówczas rozszerzyć go o anti-command.
Każde polecenie zawierające logikę zawiera w sobie anty polecenie (jako obiekt dostępny przez getter - a getter w interfejsie Command). Antypolecenie zawiera logikę odwrotną do polecenie.
Teraz w naszym przypadku Mediator wykonując polecenie, pobiera z niego anty-polecenie i odkłada je na stosie. W razie potrzeby, czyli gdy użytkownik kliknie cofnij (ctrl+z) wołamy Mediatora, który pobierze polecenie ze stosu anty-poleceń i wykonana go. Proste:)
//==============================
O ile omawiana problematyka usability w kontekście kompetencji użytkownika jest ogólna, to przedstawione rozwiązanie nie aplikuje się do aplikacji webowych z cienkim klientem. Zresztą taki klient ma zastosowanie do innej klasy problemów.
Natomiast rosnąca popularność grubszych klientów (opartych o: flex, javafx, silverligh, stare-dobre-niedoinwestowane-przez-matołów-suna applety czy nawet GWT) pozwala przewidywać, że coraz częściej będziemy spotykać się problemami tej klasy.
*Tytuł niczym wzięty ze sztandaru
*z jakiegoś pochodu
*z uprzedniej smutnej epoki:)
*/
Witam po długiej przerwie. Zaprezentuję dziś pewne ujęcie Usability oraz koncepcję technicznego rozwiązania użytecznej aplikacji - jej architekturę i dwa główne wzorce projektowe.
NOWICJUSZ vs EKSPERT
Użyteczność niejedno ma oblicze. Możemy mówić o przejrzystości, intuicyjności, ergonomii itd. Każdy z nas zapewne zdefiniuje dobre GUI jako takie, które oferuje to czego się spodziewamy. Ale co to znaczy? Oczywiście dla każdego coś innego.
Dziś skupimy się na problemie różnych potrzeb i oczekiwań użytkowników o różnym poziomie zaawansowania.
Analizując Model Rozwoju Kompetencji Braci Dreyfus
krótki opis
szerszy opis
możemy wywnioskować, że użytkownicy reprezentujący skrajne poziomy kompetencji potrzebują skrajnie odmiennego GUI.
Nowicjusz i Zaawansowany Początkujący są nastawieni na zadania ponieważ nie ogarniają jeszcze całości problemu. Potrzebują określonej ścieżki, przez którą będą prowadzeni za rękę a na każdym etapie dostaną jasne wytyczne odnośnie tego CO trzeba zrobić.
Z drugiej zaś strony Kompetentny, Biegły i Ekspert są nastawieni na osiągnięcie celu i mniej lub bardziej orientują się jak go osiągnąć.
Jak to w życiu bywa: "różni ludzie, różne potrzeby".
Mniej zaawansowani użytkownicy potrzebują aplikacji-asystenta zorientowanej na zadania. Aplikacja taka nie powinna atakować mnogością możliwości. Powinna w danym momencie sugerować (lub wręcz zmuszać) do zrobienia właściwej rzeczy. Przykładem mogą być nasze ulubione kretynatory (yyy kreatory).
Zaawansowany użytkownik potrzebuje aplikacji zorientowanej na możliwości. Dla niego wygodne będzie rozbudowane menu, bez sugerowania co kiedy kliknąć. Zaawansowany użytkownik dobrze wie co chce zrobić i nie potrzebuje aby mu podpowiadać. Mało tego, taki użytkownik ma wyrobiony swój własny styl pracy, zatem aplikacja-asystent jedynie przeszkadza. Najlepszym przykładem może być: IDE (np. Eclipse), zaawansowany edytor tekstu albo narzędzie typu CAD. Tysiące opcji w menu i biała kartka na dzień dobry.
Klasyczny problem występujący w aplikacjach biznesowych to nauka obsługi użytkowników. Stare systemy tej klasy orientowało się na możliwości (teraz powoli sytuacja ulega zmianie). Mamy zatem nieszczęsną użytkowniczkę, która zaczyna naukę obsługi systemu (może również jednocześnie "uczy się komputera"). Przytłoczenie mnogością opcji skutkuje jednym: tresurą. Użytkownik nie ma innego wyjścia jak opanowanie procesu biznesowego na zasadzie szympansa: klikam trzeci z lewej, później ok, później przedostatnia zakładka, później klikam drugi od dołu...
Wszyscy znamy też nietrafione rozwiązanie, które miało pomagać nowicjuszom w okiełzaniu eksperckiego narzędzia - spinacz w Office;)
PRZYKŁAD APLIKACJI
W przypadku aplikacji korporacyjnych jest łatwiej. Użytkownik nie ma innego wyjścia jak zacisnąć zęby i nauczyć się (pardon - wytresować się). Jest to po prostu przykre narzędzie jego pracy.
Natomiast gdy nasza aplikacja ma konkurować na wolnym rynku wówczas musimy nieco się wysilić. Użytkownik zirytowany autystycznym GUI aplikacji po prostu porzuci ją, zaklnie i pójdzie sobie do konkurencji:)
Użytkownicy są teraz niezbyt cierpliwi i mają wysokie wymagania odnośnie przyjemności - pokolenie MTV?
Aby ustalić kontekst aplikacji walczącej o klienta wysokim poziomem użyteczności wyobraźmy sobie system do projektowania wnętrz, np. wnętrz kuchni.
Niech zaprojektowanie wymaga między innymi: określenia obszaru pomieszczenia, ustalenia otworów i innych przeszkód, rozstawienia mebli i AGD, dopasowania wszystkich wymiarów, wyboru materiałów z których mają być wykonanie poszczególne elementy, itd. Dalej mamy rysunki techniczne, wyliczenia należności, umowy, itp.
Ustalmy wyzwanie: z aplikacji ma być wykorzystywana przez:
- klientów końcowych, którzy to raz na kilka(naście) lat kupują sobie "kuchnię"
- projektantów kuchni, którzy dziennie wyklikują po kilka takich zestawów.
Co z kompetencjami naszych użytkowników?
Jasne jest, że klient końcowy jest totalnym Nowicjuszem jeżeli chodzi o aplikację. Użyje jej raz (ew jako rodzinny "haker" pomoże szwagrowi wyklikać jego zestaw). Zatem nigdy nie nabędzie wyższych kompetencji. Co gorsza klient może być również niekompetentny w domenie problemu, czyli wszystkich zagadnieniach związanych z kuchniami.
Manual oczywiście odpada, bo zirytowany klient pójdzie sobie do konkurencji.
Projektant jest zwykle w miarę kompetentny w domenie problemu;) Będzie też Nowicjuszem jako użytkownik, ale ponieważ będzie miał motywację i częstą styczność z aplikacją to szybko osiągnie poziom Kompetentny a z czasem dojdzie do Eksperta.
ARCHITEKTURA ROZWIĄZANIA
Mamy zatem dwie klasy użytkowników, którzy potrzebują diametralnie różnego interfejsu. Początkujący użytkownik wymaga przeprowadzenia go przez cały proces projektowania krok po kroku, bez zbędnych pytań, defalutowo, szybko, gładko i bez irytacji. Zaawansowany użytkownik potrzebuje natomiast wolności, możliwości i tysięcy szczegółowych opcji.
Jak to pogodzić? Czy musimy tworzyć niemal 2 osobne aplikacje?
Niekoniecznie, jeżeli podejdziemy do problemu racjonalnie to możemy zredukować narzut związany z przygotowaniem 2 wersji.
Możemy podejść do problemu następująco:

Wydzielamy CORE aplikacji, który zawiera klasy odpowiedzialne za główną funkcjonalność, czyli projektowanie wnętrz (wraz ze wszystkimi regułami z tym związanymi) oraz wizualizację projektu.
Core jest dostępny przez API.
Mamy 2 zestawy GUI: jeden prosty i intuicyjny oraz zorientowany na zadania przeznaczony dla początkujących. Drugi to klasyczny potwór dla eksperta oferujący niezliczone możliwości w rozbudowanych menu.
Oba rodzaje GUI komunikują cię z Corem wysyłając polecenia zhermetyzowane przy użyciu wzorca projektowego Command. Idea wzorca jest taka, że czynności nie są metodami (jak w klasycznym podejściu proceduralnym). Czynności są obiektami. Dla wszystkich klas poleceń istnieje wspólny interfejs zawierający jedną metodę - jest to sygnał do polecenia aby wykonało ono zawartą w sobie logikę.
Konkretny command zawiera w sobie zestaw instrukcji operujących na API Cora. Oczywiście commandy wysyłane z GUI Nowicjusza będą zawierać więcej kodu operującego na API. Jedna czynność takiego użytkownika wykonuje więcej operacji. Commandy wysyłane z GUI Eksperta będą bardziej elementarne. Commandy mogą po sobie dziedziczyć, mogą się na wzajem dekorować z wykorzystaniem wzorca Dekratora w celu reużycia logiki.
W przedstawionej architekturze mamy dodatkowo wzorzec Mediatora. Został on wprowadzony jako "środowisko uruchomieniowe" dla poleceń oraz "hub" spinający wiele komponentów. GUI wysyła commadny do Mediatora, a to mediator wykonuje otrzymane polecenie (uruchamiając wspólną metodę interfejsu) przekazując do nich kontekst uruchomienia, czyli core.
Mediator może również przy okazji manipulować stanem GUI, które nie należy do Core (np nakładać przeźroczystą chmurę na czas długotrwałych operacji bądź włączać dostępność opcji w menu). Mediator może też generować zdarzenia aplikacyjne, które będą obsługiwane przez pewne komponenty graficzne prezentujące zajście danego zdarzenia. Można bawić się do woli z Mediatorem, ale trzeba uważać aby nie stał się boską klasą o zbyt dużej odpowiedzialności.
ZWIĘKSZAMY USABILITY
Każdy użyteczny system, którego idea zasadzą się na bogatej interakcji użytkownika powinien oferować funkcjonalność "Cofnij". Wprowadzenie tej funkcjonalności jest generalnie bardzo kłopotliwe. Kto zmagał się z tym problemem ten zapewne próbował niewydajnego rozwiązania polegającego na "zrzucie" aktualnego stanu do pamięci podczas wykonywania każdej operacji modyfikującej stan.
Ogólnie jest to problematyczna funkcjonalność, chyba że...

...posiłkujemy się wzorcem Command:)
Możemy wówczas rozszerzyć go o anti-command.
Każde polecenie zawierające logikę zawiera w sobie anty polecenie (jako obiekt dostępny przez getter - a getter w interfejsie Command). Antypolecenie zawiera logikę odwrotną do polecenie.
Teraz w naszym przypadku Mediator wykonując polecenie, pobiera z niego anty-polecenie i odkłada je na stosie. W razie potrzeby, czyli gdy użytkownik kliknie cofnij (ctrl+z) wołamy Mediatora, który pobierze polecenie ze stosu anty-poleceń i wykonana go. Proste:)
//==============================
O ile omawiana problematyka usability w kontekście kompetencji użytkownika jest ogólna, to przedstawione rozwiązanie nie aplikuje się do aplikacji webowych z cienkim klientem. Zresztą taki klient ma zastosowanie do innej klasy problemów.
Natomiast rosnąca popularność grubszych klientów (opartych o: flex, javafx, silverligh, stare-dobre-niedoinwestowane-przez-matołów-suna applety czy nawet GWT) pozwala przewidywać, że coraz częściej będziemy spotykać się problemami tej klasy.
wtorek, 26 stycznia 2010
Wspinaczka do profesjonalizmu
Wreszcie po ponad miesięcznej przerwie znalazłem chwilę czasu na napisanie posta:)
Brak czasu na pisanie wynika głównie z nawału nowych obowiązków związanych z opieką nad 3-tygodniową córką:]
Dlatego też dziś odsyłam do artykułu mojego autorstwa, który ukazał się w lutowym wydaniu SDJ pt:
"Wspinaczka do profesjonalizmu
Modelowa ścieżka rozwoju kompetencji – podejście pragmatyczne"
Artykuł traktuje o rozwoju kompetencji developerskich w kontekście Modelu Kompetencji Braci Dreyfus, o którym już pisałem.
W tekście zachowano znaczniki specyficzne dla wydawnictwa, ponieważ są czytelne dla "ludzi z branży" a jednocześnie nadają mu strukturę. Tekst zawiera kilkanaście stron, ale podobno szybko i lekko się go czyta;)
Zapraszam do lektury artykułu i być może nawet całego numeru poświęconego programowaniu gier - zawsze to ciekawa odmiana od enterprajz;)
//========================
W ciągu najbliższego tygodnia noworodek powinien przepoczwarzyć się w mniej zasobożerne niemowlę a co za tym idzie pojawi się kilka nowych postów. Będą poświęcone zagadnieniom z zakresu Craftsmanship, m. in. projektowaniu systemu pod kątem usability (na podstawie moich ostatnich doświadczeń z paroma autystycznymi koszmarkami) .
Brak czasu na pisanie wynika głównie z nawału nowych obowiązków związanych z opieką nad 3-tygodniową córką:]
Dlatego też dziś odsyłam do artykułu mojego autorstwa, który ukazał się w lutowym wydaniu SDJ pt:
"Wspinaczka do profesjonalizmu
Modelowa ścieżka rozwoju kompetencji – podejście pragmatyczne"
Artykuł traktuje o rozwoju kompetencji developerskich w kontekście Modelu Kompetencji Braci Dreyfus, o którym już pisałem.
W tekście zachowano znaczniki specyficzne dla wydawnictwa, ponieważ są czytelne dla "ludzi z branży" a jednocześnie nadają mu strukturę. Tekst zawiera kilkanaście stron, ale podobno szybko i lekko się go czyta;)
Zapraszam do lektury artykułu i być może nawet całego numeru poświęconego programowaniu gier - zawsze to ciekawa odmiana od enterprajz;)
//========================
W ciągu najbliższego tygodnia noworodek powinien przepoczwarzyć się w mniej zasobożerne niemowlę a co za tym idzie pojawi się kilka nowych postów. Będą poświęcone zagadnieniom z zakresu Craftsmanship, m. in. projektowaniu systemu pod kątem usability (na podstawie moich ostatnich doświadczeń z paroma autystycznymi koszmarkami) .
niedziela, 20 grudnia 2009
Java EE
Kilka dni temu ukazała się finalna wersja specyfikacji Java EE 6.
Jako, że żaden szanujący się blog Javowy nie może nie wspomnieć o tym doniosłym wydarzeniu, zatem niniejszym to czynię:
Proszę Państwa, mamy nową specyfikację Java EE!
Ok, formalności mamy za sobą. Teraz aby ten post jednak coś wnosił do sprawy i aby zachować niezależny charakter bloga pozwolę sobie polecić 2 linki stanowiące niejako rozliczenie dotychczasowych wersji platformy:
- Rod Johnson (twórca Spring framework) w prezentacji Lessons Learned From Java EE’s Evolution opowiada historię platformy, m. in. wpływ niesławnych twórców CORBY na jej kształt:) Człowiek, który zna na wyrywki całą specyfikację Java EE twierdzi, że jedyną dobrą rzeczą jest Servlet API.
- Misko Hevery (Agile Coach w Google odpowiedzialny za automatyzację testów) w swoim poście How to do Everything Wrong with Servlets twierdzi natomiast, że Servlet API jest przykładem bardzo złego designu, bardzo złego.
Aż strach pomyśleć do jakich wniosków doszliby obaj panowie dokonując koniunkcji zbiorów swoich poglądów.
//============================
Warto zwrócić uwagę na specyfikację JSF 2.0 (wchodzącą w skład Java EE 6), która wreszcie po latach będzie nieco bardziej pomocna do tworzenia aplikacji webowych. Przykładowo otrzymujemy oficjalne wsparcie dla szablonów (bez Feceletów jak bez ręki) oraz lepszą orientację na URL (taki mały szczegół webowy).
Jako, że żaden szanujący się blog Javowy nie może nie wspomnieć o tym doniosłym wydarzeniu, zatem niniejszym to czynię:
Proszę Państwa, mamy nową specyfikację Java EE!
Ok, formalności mamy za sobą. Teraz aby ten post jednak coś wnosił do sprawy i aby zachować niezależny charakter bloga pozwolę sobie polecić 2 linki stanowiące niejako rozliczenie dotychczasowych wersji platformy:
- Rod Johnson (twórca Spring framework) w prezentacji Lessons Learned From Java EE’s Evolution opowiada historię platformy, m. in. wpływ niesławnych twórców CORBY na jej kształt:) Człowiek, który zna na wyrywki całą specyfikację Java EE twierdzi, że jedyną dobrą rzeczą jest Servlet API.
- Misko Hevery (Agile Coach w Google odpowiedzialny za automatyzację testów) w swoim poście How to do Everything Wrong with Servlets twierdzi natomiast, że Servlet API jest przykładem bardzo złego designu, bardzo złego.
Aż strach pomyśleć do jakich wniosków doszliby obaj panowie dokonując koniunkcji zbiorów swoich poglądów.
//============================
Warto zwrócić uwagę na specyfikację JSF 2.0 (wchodzącą w skład Java EE 6), która wreszcie po latach będzie nieco bardziej pomocna do tworzenia aplikacji webowych. Przykładowo otrzymujemy oficjalne wsparcie dla szablonów (bez Feceletów jak bez ręki) oraz lepszą orientację na URL (taki mały szczegół webowy).
niedziela, 29 listopada 2009
Seam == JSF + EJB ?
WSTĘP
Framework Seam opiera się na bardzo silnym mechanizmie - bijekcji.
Bijekcja to nowa jakość w technice Dependency Injection, która pozwala na budowanie bardziej naturalnych konstrukcji Inversion on Control.
/**
* Na marginesie dodam, że DI i IoC to nie jest to samo.
* Wyjaśnienie różnic w tym przydługim poście:
* Wprowadzenie do wstrzykiwania zależności i Springa zarazem
*/
Bijekcja w skrócie przedstawia się następująco:
- podczas wywołania metody
- następuje wstrzyknięcie zależności do obiektu, którego metoda jest wołana (pola adnotowane @In)
- metoda jest wykonana
- następuje wystrzyknięcie obiektów do kontekstu Seam (pola adnotowane @Out)
- następuje czyszczenie (nulllowanie) wstrzykniętych zależności
Od strony technicznej odbywa się to w Seam dzięki specjalnym interceptorom. Przechwytują one wywołania metod wszystkich komponentów i wokół tych wywołań dodają całą opisaną powyżej magię.
Koncepcja jest jak już napisałem piękna, ponieważ pozwala na budowanie dużo bardziej naturalnych konstrukcji. Wstrzyknięcia następują per wywołanie metody a nie jedynie raz, po stworzeniu komponentu - zaspawane na wieki wieków (albo przynajmniej do restartu servera z powodu zwisu aplikacji;)
Dodatkowo niemal bez ograniczeń możemy wstrzykiwać w siebie komponenty o różnych zasięgach - nie ma ograniczenia szerszy zasięg w węższy. Dlatego, że wstrzyknięcie następuje jedynie na czas wywołania metody i nie będziemy mieli nigdy problemu z nieświeżą instancją komponentu.
PROBLEM
Twórcy frameworka poszli jeszcze dalej...
Umożliwiają używanie Sesyjnych EJB wprost w JSF - bez potrzeby pośredniej warstwy Managed Beanów (lub ich specyficznej odmiany Backing Beanów). Czyli komponenty Seam, które są jednocześnie komponentami EJB będą widoczne w kontekście JSF.
Managed Beany są podobno zbędą warstwą, która tylko przeszkadza. Możliwość wołania EJB z JSF jest rzekomo tryumfem rozumu na platformą korporacyjną.
Hmmm w bardzo prostych systemach klasy "Przeglądarka Bazy Danych" rzeczywiście można by się obyć bez MB, ale w niniejszym poście przedstawię do jakich kuriozalnych konstruktów dochodzi gdy w nietrywialnych przypadkach wiążemy JSF wprost z EJB.
Na wstępie zaznaczam, że na potrzeby niniejszych rozważań pomijamy aspekty warstwowej architektury, elementarnych zasad projektowania mówiących o kohezji klasy i temu podobnych staromodnych ograniczeniach. Skupiamy się na "produktywnym" kodowaniu na wyścigi rodem z najlepszych tutoriali i książek.
Zaczynamy.
Przykład prosty, klasyczny ekran prezentujący listę czegoś.
Wymagania:
Po ordynarnym wejściu na stronę przez GET chcemy aby lista wyświetlała wszystkie dane.
Widok ma pozwalać również na wyszukanie czegoś po jakimś atrybucie - czyli wpisanie szukanego słowa w pole tekstowe i naciśnięcie buttonu z zaokrąglonymi rogami "Szukaj" (POST gwoli ścisłości).
Na początek implementujemy pierwsze wymaganie: po wejściu na stronę widzimy listę wszystkiego...
Strzępek kodu widoku:
Stateless Session Bean, który dostarcza danych dla widoku:
Co się tutaj dzieje: JSF rząda komponentu listaCzegos. Seam widzi, że nie istnieje on w kontekście, ale na szczęście znalazł ochotnika, który go sfabrykuje - metodę otagowaną adnotacją @Factory("listaCzegos"). Metoda zostaje wywołana, metoda ustawia pole prywatne, a ponieważ pole jest adnotowane @Out to po chwili jest wystrzykiwane do kontekstu. Dzięki temu JSF "widzi" listę i może ją już teraz spokojnie renderować w tabelce.
Kod Session Beana mógłby równie dobrze wyglądać tak:
Ale już śpieszę wyjaśnić skąd poprzednia konstrukcja. Mam już w zamyśle spełnienie drugie wymagania - funkcjonalności wyszukiwania. Zatem na widoku pojawi się pole tekstowe i button:
Nasz Session Bean dostanie metodę search, która wyszuka dane na podstawie wstrzykniętych kryteriów, następnie wynik ustawi w prywatnym polu, z którego to po chwili wartość zostanie wystrzyknięta do kontekstu Seam, skąd JSF będzie ją widział.
Jeszcze tylko dla ścisłości komponent przechowujący kryteria wyszukiwania. Zasięg PAGE aby kryteria były widoczne po przeładowaniu strony:
Pytanie: co jest nie tak z poniższym kodem?
Dla ułatwienia wyrzucę linijki odwracające uwagę i zaznaczę kluczowy element:
(To przecież nie jest egzamin na certyfikat - chcemy się tu dowiedzieć czegoś pożytecznego)
Właśnie!
Niby mamy bezstanowy komponent, ale korzystamy z niego w stanowy sposób!
Wyobraźmy sobie, że nasz wspaniały komponent biznesowy jest tak genialny, że chcemy go wykorzystać jeszcze gdzieś poza JSF, np wywołać zdalnie. Musimy wówczas:
1. ustawić kryteria wyszukiwania (zakładając, że mamy setter)
2. odpalić metodę search(), która zmieni stan - tu jest ta nieszczęsna stanowość
3. odebrać wynik przez getter
Czyli JSF wymusza na nas styl "strzelania z muszkietu": załaduj i wypal.
Czy dałoby się wykorzystać mimo wszystko ten super-kod poza Seam, np przykrywając go fasadą:
Niestety NIE, ponieważ nie mamy gwarancji, że kontener JEE przy każdym z 3 wywołań bezstanowego komponentu zaserwuje nam tą samą instancję!
Dlaczego ten bezsensowny kod działa w ogóle w Seam? Tak jak napisałem na wstępie - interceptory Seam. Jeden z nich przechwytuje wołanie metody search na komponencie, wstrzykuje kryteria, wykonuje metodę, wystrzykuje wynik. Ponieważ wstrzykiwanie i wystrzykiwanie nie są wywołaniem metod biznesowych bezstanowego komponentu to interceptor cały czas operuje na tej samej instancji.
ROZWIĄZANIE
Łatwo można rozwiązać problem sensowności kodu zmieniając bean bezstanowy na stanowy. Jednak wciąż mamy problem z bezsensownością logiczną. Dlaczego jakiś komponent będący de facto wrapperem dla procedury ma być stanowy?
Owszem w pewnych sytuacjach stanowość może mieć sens, np: z przyczyn wydajnościowych komponent stanowy trzyma wynik jako jakiś kursor po stronie bazy. Klient Stanowego Komponentu Sesyjnego przegląda listę wynikową po kawałku. Tę argumentację zaliczam.
Innym usprawiedliwieniem może być chęć wybrania (kliknięcia) wiersza - z technicznych powodów musi wówczas przechować listę. Innym jeszcze usprawiedliwieniem może być naiwna paginacja, która w naiwnych paginatorach działa na danych sesyjnych pobranych w całości z bazy.
Czy jednak sensowne jest dopasowywanie API komponentów biznesowych do takich szczegółów technicznych jakiś frameworków prezentacji?
Poza tym wciąż będziemy mieli kuriozalne korzystanie z niego w fasadzie - w stylu "strzelania z muszkietu": załaduj i wypal.
PRAWDZIWE ROZWIĄZANIE
Aby nasze komponenty biznesowe miały sensowny interfejs w nietrywialnych przypadkach musimy ponieść ten niesamowity trud wprowadzenia warstwy jakiś Managed Beanów - w Seam zwanych Akcjami.
Są to zwykłe POJOs, które mają API w stylu "strzelania z muszkietu" a logikę biznesową delegują do EJB:
Jest to również doskonałe miejsce do wstrzyknięcia np kontekstu FacesMessages, kontekstów Seam, parametrów Request, bindowanie UIComponent i innych zależności typowych dla technikaliów frameworków. W tej warstwie możemy sobie na to śmiało pozwolić i dzięki niej nie musimy brudzić EJB zależnościami od Seam i JSF.
//==========================
Sama możliwość wołania Sesyjnych EJB z JSF jest oczywiście bardzo wygodnym ficzerem i warto czasem z niej korzystać. Ale tylko wówczas gdy ma to sens i jest racjonalnie uzasadnione.
Tworzenie komponentu biznesowego, który jest "zbrukany" stylem i zależnościami pewnych technologii powoduje, że nie jest to już ani komponent ani biznesowy. Komponent - czyli pewna reużywalna część; biznesowy - czyli zajmujący się jedynie logiką biznesową.
Framework Seam opiera się na bardzo silnym mechanizmie - bijekcji.
Bijekcja to nowa jakość w technice Dependency Injection, która pozwala na budowanie bardziej naturalnych konstrukcji Inversion on Control.
/**
* Na marginesie dodam, że DI i IoC to nie jest to samo.
* Wyjaśnienie różnic w tym przydługim poście:
* Wprowadzenie do wstrzykiwania zależności i Springa zarazem
*/
Bijekcja w skrócie przedstawia się następująco:
- podczas wywołania metody
- następuje wstrzyknięcie zależności do obiektu, którego metoda jest wołana (pola adnotowane @In)
- metoda jest wykonana
- następuje wystrzyknięcie obiektów do kontekstu Seam (pola adnotowane @Out)
- następuje czyszczenie (nulllowanie) wstrzykniętych zależności
Od strony technicznej odbywa się to w Seam dzięki specjalnym interceptorom. Przechwytują one wywołania metod wszystkich komponentów i wokół tych wywołań dodają całą opisaną powyżej magię.
Koncepcja jest jak już napisałem piękna, ponieważ pozwala na budowanie dużo bardziej naturalnych konstrukcji. Wstrzyknięcia następują per wywołanie metody a nie jedynie raz, po stworzeniu komponentu - zaspawane na wieki wieków (albo przynajmniej do restartu servera z powodu zwisu aplikacji;)
Dodatkowo niemal bez ograniczeń możemy wstrzykiwać w siebie komponenty o różnych zasięgach - nie ma ograniczenia szerszy zasięg w węższy. Dlatego, że wstrzyknięcie następuje jedynie na czas wywołania metody i nie będziemy mieli nigdy problemu z nieświeżą instancją komponentu.
PROBLEM
Twórcy frameworka poszli jeszcze dalej...
Umożliwiają używanie Sesyjnych EJB wprost w JSF - bez potrzeby pośredniej warstwy Managed Beanów (lub ich specyficznej odmiany Backing Beanów). Czyli komponenty Seam, które są jednocześnie komponentami EJB będą widoczne w kontekście JSF.
Managed Beany są podobno zbędą warstwą, która tylko przeszkadza. Możliwość wołania EJB z JSF jest rzekomo tryumfem rozumu na platformą korporacyjną.
Hmmm w bardzo prostych systemach klasy "Przeglądarka Bazy Danych" rzeczywiście można by się obyć bez MB, ale w niniejszym poście przedstawię do jakich kuriozalnych konstruktów dochodzi gdy w nietrywialnych przypadkach wiążemy JSF wprost z EJB.
Na wstępie zaznaczam, że na potrzeby niniejszych rozważań pomijamy aspekty warstwowej architektury, elementarnych zasad projektowania mówiących o kohezji klasy i temu podobnych staromodnych ograniczeniach. Skupiamy się na "produktywnym" kodowaniu na wyścigi rodem z najlepszych tutoriali i książek.
Zaczynamy.
Przykład prosty, klasyczny ekran prezentujący listę czegoś.
Wymagania:
Po ordynarnym wejściu na stronę przez GET chcemy aby lista wyświetlała wszystkie dane.
Widok ma pozwalać również na wyszukanie czegoś po jakimś atrybucie - czyli wpisanie szukanego słowa w pole tekstowe i naciśnięcie buttonu z zaokrąglonymi rogami "Szukaj" (POST gwoli ścisłości).
Na początek implementujemy pierwsze wymaganie: po wejściu na stronę widzimy listę wszystkiego...
Strzępek kodu widoku:
<h:dataTable value="#{listaCzegos}" var="_cos">
<h:column>#{_cos.nazwa}</h:column>
...
</h:dataTable>
Stateless Session Bean, który dostarcza danych dla widoku:
@Stateless
@Name("cosProwajder")
public class CosProwajderBean implements CosProwajderLocal{
@Out
private ListlistaCzegos;
@Factory("listaCzegos")
public void initListaCzegos(){
listaCzegos = //pobranie danych
}
}
Co się tutaj dzieje: JSF rząda komponentu listaCzegos. Seam widzi, że nie istnieje on w kontekście, ale na szczęście znalazł ochotnika, który go sfabrykuje - metodę otagowaną adnotacją @Factory("listaCzegos"). Metoda zostaje wywołana, metoda ustawia pole prywatne, a ponieważ pole jest adnotowane @Out to po chwili jest wystrzykiwane do kontekstu. Dzięki temu JSF "widzi" listę i może ją już teraz spokojnie renderować w tabelce.
Kod Session Beana mógłby równie dobrze wyglądać tak:
@Stateless
@Name("cosProwajder")
public class CosProwajderBean implements CosProwajderLocal{
@Factory("listaCzegos")
public ListinitListaCzegos(){
listaCzegos = //pobranie danych
return listaCzegos;
}
}
Ale już śpieszę wyjaśnić skąd poprzednia konstrukcja. Mam już w zamyśle spełnienie drugie wymagania - funkcjonalności wyszukiwania. Zatem na widoku pojawi się pole tekstowe i button:
<h:form>
<h:inputText value="#{searchFilter.name}" />
<h:commandButton action="#{cosProwajder.search}" />
<h:form>
Nasz Session Bean dostanie metodę search, która wyszuka dane na podstawie wstrzykniętych kryteriów, następnie wynik ustawi w prywatnym polu, z którego to po chwili wartość zostanie wystrzyknięta do kontekstu Seam, skąd JSF będzie ją widział.
@Stateless
@Name("cosProwajder")
public class CosProwajderBean implements CosProwajderLocal{
@Out
private ListlistaCzegos;
@In
private SearchCriteria searchCriteria;
@Factory("listaCzegos")
public void initListaCzegos(){
listaCzegos = //pobranie danych
}
public void search(){
listaCzegos = //pobranie danych na podstawie searchCriteria
}
}
Jeszcze tylko dla ścisłości komponent przechowujący kryteria wyszukiwania. Zasięg PAGE aby kryteria były widoczne po przeładowaniu strony:
@Name("searchCriteria")
@Scope(PAGE)
public class SearchCriteria implements Serializable{
private String name;
//getter i setter
}
Pytanie: co jest nie tak z poniższym kodem?
@Stateless
@Name("cosProwajder")
public class CosProwajderBean implements CosProwajderLocal{
@Out
private ListlistaCzegos;
@In
private SearchCriteria searchCriteria;
@Factory("listaCzegos")
public void initListaCzegos(){
listaCzegos = //pobranie danych
}
public void search(){
listaCzegos = //pobranie danych na podstawie searchCriteria
}
}
Dla ułatwienia wyrzucę linijki odwracające uwagę i zaznaczę kluczowy element:
(To przecież nie jest egzamin na certyfikat - chcemy się tu dowiedzieć czegoś pożytecznego)
@Stateless //<<-------------
public class CosProwajderBean implements CosProwajderLocal{
private ListlistaCzegos;
private SearchCriteria searchCriteria;
public void search(){
listaCzegos = //pobranie danych na podstawie searchCriteria
}
}
Właśnie!
Niby mamy bezstanowy komponent, ale korzystamy z niego w stanowy sposób!
Wyobraźmy sobie, że nasz wspaniały komponent biznesowy jest tak genialny, że chcemy go wykorzystać jeszcze gdzieś poza JSF, np wywołać zdalnie. Musimy wówczas:
1. ustawić kryteria wyszukiwania (zakładając, że mamy setter)
2. odpalić metodę search(), która zmieni stan - tu jest ta nieszczęsna stanowość
3. odebrać wynik przez getter
Czyli JSF wymusza na nas styl "strzelania z muszkietu": załaduj i wypal.
Czy dałoby się wykorzystać mimo wszystko ten super-kod poza Seam, np przykrywając go fasadą:
@Stateless
public class CosProwajderFacadeBean implements CosProwajderFacadeRemote{
@Ejb
private CosProwajderLocal cosProwajder;
public Listsearch(SearchCriteria searchCriteria){
cosProwajder.setSearchCriteria(searchCriteria);
cosProwajder.search();
return cosProwajder.getListaCzegos();
}
}
Niestety NIE, ponieważ nie mamy gwarancji, że kontener JEE przy każdym z 3 wywołań bezstanowego komponentu zaserwuje nam tą samą instancję!
Dlaczego ten bezsensowny kod działa w ogóle w Seam? Tak jak napisałem na wstępie - interceptory Seam. Jeden z nich przechwytuje wołanie metody search na komponencie, wstrzykuje kryteria, wykonuje metodę, wystrzykuje wynik. Ponieważ wstrzykiwanie i wystrzykiwanie nie są wywołaniem metod biznesowych bezstanowego komponentu to interceptor cały czas operuje na tej samej instancji.
ROZWIĄZANIE
Łatwo można rozwiązać problem sensowności kodu zmieniając bean bezstanowy na stanowy. Jednak wciąż mamy problem z bezsensownością logiczną. Dlaczego jakiś komponent będący de facto wrapperem dla procedury ma być stanowy?
Owszem w pewnych sytuacjach stanowość może mieć sens, np: z przyczyn wydajnościowych komponent stanowy trzyma wynik jako jakiś kursor po stronie bazy. Klient Stanowego Komponentu Sesyjnego przegląda listę wynikową po kawałku. Tę argumentację zaliczam.
Innym usprawiedliwieniem może być chęć wybrania (kliknięcia) wiersza - z technicznych powodów musi wówczas przechować listę. Innym jeszcze usprawiedliwieniem może być naiwna paginacja, która w naiwnych paginatorach działa na danych sesyjnych pobranych w całości z bazy.
Czy jednak sensowne jest dopasowywanie API komponentów biznesowych do takich szczegółów technicznych jakiś frameworków prezentacji?
Poza tym wciąż będziemy mieli kuriozalne korzystanie z niego w fasadzie - w stylu "strzelania z muszkietu": załaduj i wypal.
PRAWDZIWE ROZWIĄZANIE
Aby nasze komponenty biznesowe miały sensowny interfejs w nietrywialnych przypadkach musimy ponieść ten niesamowity trud wprowadzenia warstwy jakiś Managed Beanów - w Seam zwanych Akcjami.
Są to zwykłe POJOs, które mają API w stylu "strzelania z muszkietu" a logikę biznesową delegują do EJB:
@Name("cosControler")
public class CosControler{
@In //EJB
private CosProwajderLocal cosProwajder;
@Out
private ListlistaCzegos;
@In
private SearchCriteria searchCriteria;
@Factory("listaCzegos")
public void initListaCzegos(){
//używamy pustych kryteriów (w celu optymalizacji można by wynieść je do singeltona)
listaCzegos = cosProwajder.search(new SearchCriteria());
}
public void search(){
//Wywolanie EJB
listaCzegos = cosProwajder.search(searchCriteria);
}
}
Jest to również doskonałe miejsce do wstrzyknięcia np kontekstu FacesMessages, kontekstów Seam, parametrów Request, bindowanie UIComponent i innych zależności typowych dla technikaliów frameworków. W tej warstwie możemy sobie na to śmiało pozwolić i dzięki niej nie musimy brudzić EJB zależnościami od Seam i JSF.
//==========================
Sama możliwość wołania Sesyjnych EJB z JSF jest oczywiście bardzo wygodnym ficzerem i warto czasem z niej korzystać. Ale tylko wówczas gdy ma to sens i jest racjonalnie uzasadnione.
Tworzenie komponentu biznesowego, który jest "zbrukany" stylem i zależnościami pewnych technologii powoduje, że nie jest to już ani komponent ani biznesowy. Komponent - czyli pewna reużywalna część; biznesowy - czyli zajmujący się jedynie logiką biznesową.
środa, 25 listopada 2009
Geneza i ograniczenia OO
Dziś jedynie dwa linki, które uważam za godne polecenia.
Przeczytałem niedawno tekst, który lekko zachwiał moim światopoglądem: Aristotle's Error or Agile Smagile. Tekst traktuje ogólnie o historii - znajdziecie tam również rzekomą genezę Object Oriented.
Rzekomo pierwotnie wcale nie chodziło o modelowanie świata, lecz o zabawy ze stertą - co będzie gdy zamiast chwilowo przechowywać zmienne na stosie wrzucimy je na dłużej do sterty.
Teorię OO Analysis i OO Design rzekomo "dorobiono" później. Tak, tak, wiem - sam jestem w szoku podobnym do tego gdy wydedukowałem, że nie ma żadnego św Mikołaja;P
Nie wiem na ile historia o pierwocinach OO jest prawdziwa, ale tym razem wielki szacunek dla Uncle Boba za zdystansowane podejście.
Kolejny link to ciekawa prezentacja człowieka, któremu doświadczenie również pozwala zdystansować się od mainstreamu Are We There Yet?. Prezentacja pozwala uświadomić sobie ograniczenia OO oraz wyjaśnia (tym, którzy jeszcze tego nie widzą) dlaczego niektóre wydawałoby się proste języki wprowadzają wysoką złożoność (przypadkową).
Zobaczycie też jak bardzo podobne są do siebie języki, o które często spieramy się "który lepszy". Nie brakuje też aluzji do takich "przełomowych usprawnień" jak usunięcie średników:) Dowiecie się również z czego wg autora prezentacji wynika potrzeba posiadania testów:)
//=======================
Od razu ostrzegam, że treści kryjące się pod linkami są mocno filozoficzne, ale może warto oderwać się na chwilę od kodu aby później móc spojrzeć na niego inaczej...
Przeczytałem niedawno tekst, który lekko zachwiał moim światopoglądem: Aristotle's Error or Agile Smagile. Tekst traktuje ogólnie o historii - znajdziecie tam również rzekomą genezę Object Oriented.
Rzekomo pierwotnie wcale nie chodziło o modelowanie świata, lecz o zabawy ze stertą - co będzie gdy zamiast chwilowo przechowywać zmienne na stosie wrzucimy je na dłużej do sterty.
Teorię OO Analysis i OO Design rzekomo "dorobiono" później. Tak, tak, wiem - sam jestem w szoku podobnym do tego gdy wydedukowałem, że nie ma żadnego św Mikołaja;P
Nie wiem na ile historia o pierwocinach OO jest prawdziwa, ale tym razem wielki szacunek dla Uncle Boba za zdystansowane podejście.
Kolejny link to ciekawa prezentacja człowieka, któremu doświadczenie również pozwala zdystansować się od mainstreamu Are We There Yet?. Prezentacja pozwala uświadomić sobie ograniczenia OO oraz wyjaśnia (tym, którzy jeszcze tego nie widzą) dlaczego niektóre wydawałoby się proste języki wprowadzają wysoką złożoność (przypadkową).
Zobaczycie też jak bardzo podobne są do siebie języki, o które często spieramy się "który lepszy". Nie brakuje też aluzji do takich "przełomowych usprawnień" jak usunięcie średników:) Dowiecie się również z czego wg autora prezentacji wynika potrzeba posiadania testów:)
//=======================
Od razu ostrzegam, że treści kryjące się pod linkami są mocno filozoficzne, ale może warto oderwać się na chwilę od kodu aby później móc spojrzeć na niego inaczej...
Subskrybuj:
Posty (Atom)