You are on page 1of 84

���������������������������������

�����������������������������������

�����������������������������������������������������������������������������
�����������������������������������������������������������������������������
�������������������������������������������������������������������������������
����������������������������

���������������������������������������������������������������������������
��������������������������������������������������������������������������������
������������������

�����������������������������������������������������������������������������������
��������������������������������������������������������������������������������
���������������������������������������������������������������������������������
������������������������������������������������������������������������������

���������������������������������������������

�����������������������������������������������������������������������������������������������
12/2009 (180)

SPIS TREŚCI
27 Opis DVD
PROGRAMOWANIE JAVA
06 Aktualności
Rafał Kocisz 28 Przewodnik po SCJP
– Czyli certyfikat z Javy – część 1
Krzysztof Rychlicki – Kicior
Proces zdobywania certyfikatów, potwierdzających umiejętno-
BIBLIOTEKA MIESIĄCA ści z różnych dziedzin wiedzy, stał się jednym z ważniejszych
elementów osobistego rozwoju. Proces ten ma miejsce również
12 Google Collections Library w branży IT; certyfikaty dla programistów (Java lub .NET), admini-
– Eleganckie i efektywne kolekcje w Javie stratorów czy sieciowców (Cisco) można coraz częściej odnaleźć
Rafał Kocisz w CV osób starających się o pracę.
Kolekcje to nieodłączny element skrzynki narzędziowej każdego
programisty. Jeśli programujesz w Javie i chciałbyś uprościć oraz
zoptymalizować Twój kod odpowiedzialny za obsługę kolekcji, to 36 BlockingQueue w Javie – Prezentacja interfej-
trafiłeś na właściwy artykuł. Zapraszamy do lektury! su BlockingQueue, kilku możliwych jego wyko-
rzystań, oraz słów kilka o programowaniu wielo-
wątkowym w Javie
Dariusz 'Scythe' Wawer
Synchronizowana kolejka to podstawa wielu aplikacji wielowąt-
KLUB TECHNICZNY kowych – prawdopodobnie większość z nas kiedyś taką zaim-
plementowała. Pytanie jednak brzmi: po co? Skoro często war-
16 Połączenie Flex oraz Flash CS3/CS4 to po prostu sięgnąć po gotowe rozwiązanie, takie jak Blockin-
– Szybkie tworzenie własnych komponentów gQueue.
Mateusz Małczak
Poznajemy rozszerzenie dla programu Flash CS3/CS4 pozwalają-
ce na szybkie tworzenie komponentów oraz kontenerów dla fra-
meworka Flex. Wykorzystamy podejście niewymagające znajo-
mości architektury komponentów, a pozwalające znacznie wzbo-
gacić nasze aplikacje.
JĘZYKI PROGRAMOWANIA
40 Delphi i C++Builder 2010 – Nowości wprowa-
dzone w Delphi© 2010 i C++Builder© 2010
20 Technologie Progress OpenEdge Bogdan Polak
– Część 4. Serwer Aplikacji OpenEdge Środowisko Delphi powstało w roku 1995, C++Builder dwa lata
Piotr Tucholski później. Mimo upływu czasu ich architektura (komponenty VCL,
Serwer Aplikacji (AppServer) OpenEdge jest potężnym silnikiem programowanie wizualne) nadal zapewnia przewagę nad inny-
transakcyjnym opartym na nowoczesnych standardach. Zapewnia mi platformami programistycznymi, w obszarze budowy aplika-
bezpieczne zarządzanie b. dużymi transakcjami, niezależność od cji okienkowych dla systemu Windows.
interfejsu użytkownika oraz możliwość stworzenia aplikacji zgod-
nej ze standardami architektury zorientowanej na serwisy (SOA).

PROGRAMOWANIE
PROGRAMOWANIE C++ URZĄDZEŃ MOBILNYCH
24 Metaprogramowanie
– Algorytmy wykonywane w czasie kompilacji 46 J2ME: Bluetooth i MMAPI
Robert Nowak – Czyli Bluetooth i MMAPI w jednym stali domu
Metaprogramowaniem nazywa się tworzenie programów, które Szymon Ulewicz
w wyniku działania dostarczają programów. Metaprogramy sto- Niewiele osób zdaje sobie sprawę z możliwości swoich telefonów
sujemy aby zwiększyć szybkość działania programów oraz ich komórkowych. W tym artykule zaznajomimy się z dwiema biblio-
czytelność, a także aby unikać powielania kodu, wtedy gdy te sa- tekami dostępnymi dla platformy J2ME, które posiadają ogrom
me operacje chcemy wykonać dla grupy typów. możliwych zastosowań.

4 12/2009
WARSZTATY
Miesięcznik Software Developer’s Journal (12 numerów w roku)
60 Czyń CUDA (część 1) – Architektura jest wydawany przez Software Press Sp. z o.o. SK
Michał Matuszak, Jacek Matulewski
GPGPU to skrót, który na ustach informatyków pojawia się co-
Redaktor naczelny:
raz częściej. Oznacza general-purpose computing on graphics Łukasz Łopuszański lukasz.lopuszanski@software.com.pl
processing units, czyli możliwość przeprowadzania dowolnych
silnie zrównoleglonych obliczeń na procesorach kart graficz- Projekt okładki: Agnieszka Marchocka
nych, których spora moc była do tej pory wykorzystywana je-
dynie do generowania grafiki trójwymiarowej, czyli w wielu Skład i łamanie:
Monika Grotkowska monika.grotkowska@software.com.pl
przypadkach okazjonalnie.

Kierownik produkcji:
68 AJAX w jQuery – Jak stworzyć efektowną gale- Andrzej Kuca andrzej.kuca@software.com.pl
rię w AJAX-ie z wykorzystaniem frameworka jQu-
ery cz. 1 Dział produkcji i kolportażu:
Alina Stebakow alina.stebakow@software.com.pl
Leszek Sewastianowicz
Coraz więcej stron w Internecie wykorzystuje technologię AJAX.
Jej znajomość nie jest już wyjątkowym atutem programisty, a co-
raz częściej jedną z podstawowych umiejętności wymaganych
praco- i zleceniodawców. Można też znaleźć wiele rozwiązań uła- Nakład: 6 000 egz.
twiających pisanie programów wykorzystujących AJAX-a. Jed-
Adres korespondencyjny:
nym z nich jest framework jQuery. Software Press Sp. z o.o. SK,
ul. Bokserska 1, 02-682 Warszawa, Polska
tel. +48 22 427 36 91, fax +48 22 224 24 59
www.sdjournal.org cooperation@software.com.pl

EFEKTYWNOŚĆ PRACY
72 Skromny programista Dział reklamy: adv@software.com.pl
– O nieprzecenianiu własnych możliwości
Obsługa prenumeraty: EuroPress Polska
Michał Bartyzel, Mariusz Sieraczkiewicz software@europress.pl
Ponad trzydzieści lat temu Edgar Dijkstra w swoim przemówieniu
The humble programmer stwierdził, że ludzkie czaszki są zbyt ma- Dołączoną do magazynu płytę CD przetestowano programem
AntiVirenKit firmy G DATA Software Sp. z o.o.
łe, by poradzić sobie z problemami programistycznymi. Zadziwia-
jące jest to, jak praktyczny wydźwięk ma to stwierdzenie. Redakcja dokłada wszelkich starań, by publikowane w piśmie
i na towarzyszących mu nośnikach informacje i programy były
poprawne, jednakże nie bierze odpowiedzialności za efekty
wykorzystania ich; nie gwarantuje także poprawnego działania
programów shareware, freeware i public domain.

AKADEMIA UML Uszkodzone podczas wysyłki płyty wymienia redakcja.

Wszystkie znaki firmowe zawarte w piśmie są własności


76 Liczebności klas uczestniczących w powiązaniach odpowiednich firm.
Zostały użyte wyłącznie w celach informacyjnych.
Szymon Zioło
Określenie liczebności klas biorących udział w powiązaniach na Redakcja używa systemu automatycznego składu
diagramach klas to jedna z najważniejszych decyzji analitycz-
nych, mająca niebagatelny wpływ na funkcjonalność modelowa- Osoby zainteresowane współpracą prosimy o kontakt:
nego systemu. Zobaczmy więc, jak poprawnie określać liczebno- cooperation@software.com.pl
ści, aby uniknąć przykrych niespodzianek.
Druk: Artdruk www.artdruk.com

Wysokość nakładu obejmuje również dodruki. Redakcja nie


udziela pomocy technicznej w instalowaniu i użytkowaniu
programów zamieszczonych na płycie CD-ROM dostarczonej
razem z pismem.

Sprzedaż aktualnych lub archiwalnych numerów pisma po


innej cenie niż wydrukowana na okładce – bez zgody wydawcy
– jest działaniem na jego szkodę i skutkuje odpowiedzialnością
sądową.

www.sdjournal.org 5
Aktualności

Native Client
jest już częścią Google Chrome Barrelfish:
Uruchomienie i wykorzystanie stworzo-
nej przez Google'a wtyczki Native Client system operacyjny 21-go
wieku rodem z Microsoftu?
nie należało do spraw prostych – dla-
tego też dla większości użytkowników
możliwość zagrania w Quake czy uru-

W
chomienie innych obliczeniowo wyma- indows 7 jest takim systemem
gających aplikacji z poziomu przeglą-
operacyjnym, jakim od począt-
darki była jedynie ciekawostką. Być
może teraz się to zmieni – Mountain ku powinna być Vista. Trudno
View wydało wersję Google Chrome ze jednak nie zauważyć, że wszelkie opisy no-
zintegrowanym Native Clientem. Native wości w Windows 7 nie koncentrowały się
Client został po raz pierwszy zaprezen- tak naprawdę na systemie operacyjnym,
towany w grudniu 2008 roku. W czerw-
ale na czymś, co z punktu widzenia archi-
cu br. osiągnął status platformy dewelo-
perskiej. tektury oprogramowania jest bez znacze-
Wtyczka pozwala na uruchamianie apli- nia – interfejsie użytkownika. Redmond
kacji webowych z bezpośrednim dostę- potrafi jednak stworzyć więcej niż cukier-
pem do procesora – dzięki temu moż- kowe interfejsy. Jego zespół badawczy za- maitych architektur sprzętowych. Zespół
liwe będzie uruchamianie w przeglą-
prezentował bardzo ciekawy nowy system uczonych: Andrew Baumann, Paul Bar-
darce bardzo wymagających progra-
mów, np. edytorów wideo czy gier. operacyjny dla wielordzeniowych maszyn ham, Pierre-Evariste Dagand, Tim Harris,
Wersja 4.0.220.1 Chrome po raz pierw- o nazwie Barrelfish. Nad projektem praco- Rebecca Isaacs, Simon Peter, Timothy Ro-
szy wprowadza w edycji dla Windows wali uczeni z laboratoriów Microsoft Rese- scoe, Adrian Schüpbach i Akhilesh Singha-
Native Clienta – można w niej bez żad- arch w Wielkiej Brytanii i politechniki w nia twierdzi, że komputery w coraz więk-
nych dodatkowych czynności urucho-
Zurichu. Efekt ich prac w ogóle nie przy- szym stopniu przypominają systemy roz-
mić testowe aplikacje, korzystające z
binarnego kodu. Wprowadzenie wtycz- pomina Windows – to praktyczne ćwicze- proszone i powinny być właśnie w taki spo-
ki jako standardowego komponen- nie z realizacji koncepcji multikernela – w sób programowane. Rozwiązaniem jest Bar-
tu Chrome'a dla Windows było możli- którym poszczególne rdzenie procesora są relfish. Zastępuje on architekturę współ-
we dzięki szeroko zakrojonym audy- traktowane jako sieć niezależnych rdze- dzielonej pamięci znacznie uproszczonym
tom bezpieczeństwa tej technologii.
ni, podobnie jak to się dzieje w systemach schematem, w którym każdy rdzeń ma de
Oferuje ona mechanizm piaskownicy,
który nie pozwala uruchamianym apli- rozproszonych. Do tej pory w konstrukcji facto własny OS i wyłączną dla siebie pa-
kacjom na uzyskanie nieuprawnione- wielordzeniowych maszyn od Intela, AMD mięć. Wszystko, co każdy rdzeń powinien
go dostępu do zasobów systemu ope- czy Suna kładło się nacisk na możliwość wiedzieć o innych, pojawia się na wspólnej
racyjnego. Początkowo implementa- dostępu każdego rdzenia do współdzielo- dla nich szynie, wyświetlającej wiadomości
cja ta miała liczne błędy – ale Google
nej pamięci i rozwijało technologie takie otrzymane od rdzeni. Badacze podkreśla-
wezwało czołowych hakerów, aby szu-
kali błędów w tej technologii, oferując jak HyperTransport, pozwalające właśnie ją, że konstrukcja taka nie wyklucza dzie-
im pokaźne nagrody za odnalezienie na zarządzanie tym procesem. Jednak bez lenia pamięci pomiędzy rdzeniami, jedy-
usterek. Ostatecznie wyeliminowano 22 zastosowania technik programowania rów- nie ustala, że działanie systemu od tego nie
groźne luki, zanim technologię uznano noległego nierzadko okazywało się, że po- zależy. Trzy fundamentalne założenia Bar-
za bezpieczną.
szczególne programy działają wolniej na relfisha to zatem ujawnienie całej komuni-
Mimo to Native Client domyślnie jest
wyłączony w najnowszym Chrome. procesorach wielordzeniowych, niż na kla- kacji między rdzeniami, traktowanie sta-
Osoby, które chcą poeksperymentować sycznych jednordzeniowych. Dlatego np. nów jako zreplikowanych, a nie współdzie-
z tą technologią, powinny skorzystać ze AMD zaczęło rozwijać techniki tzw. lek- lonych, i uczynienie całej struktury OS-a
wskazówek znajdujących się na stronie kiego profilowania (LWP), które pozwalały neutralną względem architektury sprzę-
wiki projektu.
na zwiększenie wydajności oprogramowa- towej. Według twórców takie jednordze-
Oprócz Native Clienta, nowe Chrome
przynosi kilka innych zmian i popra- nia na wielordzeniowych maszynach. Jed- niowe jądra, wdrożone na rozproszonym
wek, m.in. lepsze działanie Omnibok- nak zespół badaczy Microsoftu jest zdania, systemie, pozwalają na osiągnięcie znacz-
sa i trybu Incognito czy umożliwienie że to ślepa uliczka. W artykule pt. The Mul- nie większej neutralności względem plat-
umieszczania przycisków rozszerzeń tikernel: A new OS architecture for scalable formy sprzętowej, niż jest to obecnie. Prze-
na pasku narzędzi. Jak widać, Google
multicore systems (http://www.barrelfish.org/ nosząc system np. z intelowskiego Xeona
jest coraz bliższe realizacji swojej wizji
uczynienia z przeglądarki superszyb- barrelfish_sosp09.pdf) twierdzą oni, że na Niagarę Suna, wystarczy jedynie zmie-
kiej platformy uruchomieniowej dla przeniesienie tradycyjnych funkcjonalno- nić sposób połączenia ze sobą poszcze-
aplikacji webowych, które w niczym ści systemu operacyjnego na rozproszo- gólnych komponentów, bez przeprojekto-
nie będą ustępowały ich desktopowym ny system procesów, które komunikują się wywania poszczególnych kerneli. Pierw-
odpowiednikom. Native Client wraz z
przez przekazywanie wiadomości i w któ- szą migawkę systemu Barrelfish twórcy
wtyczką O3D, pozwalającą na bezpo-
średni dostęp do hardware'u karty gra- rym nie zachodzi wymiana danych mię- udostępnili na wolnej licencji BSD. Moż-
ficznej, pozwolą Chrome na urucha- dzy rdzeniami na najniższym poziomie, na ją pobrać ze strony www.barrelfish.org/
mianie nie tylko prostych pakietów pozwala na znaczne zwiększenie wydaj- release_20090914.html.
biurowych czy klientów poczty – ale ności względem dotychczasowych rozwią- http://www.osnews.com/
też np. nieliniowych edytorów wideo
zań. Wszystko to jest też bardziej skalowal- http://barrelfish.org/
czy gier 3D.
http:/dev.chromium.org ne i łatwiejsze w przystosowaniu do roz-

6 12/2009
Aktualności

Programowanie w stylu Kompatybilne z Windows 7?


Kompatybilne z wersjami 64-bit!

Agile coraz popularniejsze.


Redmond pokazało, że zależy mu na popu-
laryzacji 64-bitowej wersji swojego najnow-
szego systemu. Zgodnie z informacjami,

A
nalityk Forrestera, Dave West, wiesz- częściej odrzucony na rzecz modelu, mówiącego które przedstawił na korporacyjnym blogu
czy zmierzch klasycznych metodologii o regularnym i szybkim dostarczaniu oprogra- Mark Relph, szef zespołu Windows Ecosys-
oprogramowania, twierdząc, że przy- mowania, większym zaangażowaniu klientów tem, prawo do korzystania z logo Compati-
ble with Windows 7 otrzymają tylko te pro-
szłość należy do podejścia znanego jako Agile. Z i odmiennej organizacji zespołów. Najtrudniej
dukty, które będą działać ze wszystkimi
drugiej strony promujący w Australii Agile od kil- za tym nadążyć dużym korporacjom, obawia- wersjami Windows 7 – w tym z wersjami
ku lat John Dalton ostrzega przed pochopnym z jącym się świata, w którym poszczególne zmia- 64-bitowymi. Procesory 64-bitowe są mon-
niego korzystaniem. Jakie zatem faktycznie Agi- ny w projekcie nie są zatwierdzane w trzech ko- towane w większości komputerów dostęp-
le ma znaczenie dla deweloperów naszych cza- piach przez klientów. Jednak mniejsze firmy czu- nych na rynku. Wciąż jednak znaczna część
użytkowników korzysta z systemów 32-bito-
sów? Przypomnijmy, że w 2001 roku w Sieci po- ją się z tym znakomicie, szczególnie, że do dyspo-
wych. Powodem są m.in. problemy z opro-
jawił się Manifest Zwinnego Wytwarzania Opro- zycji mają narzędzia do programowania Agile, gramowaniem w systemach 64-bitowych
gramowania (Agile). Stworzony przez speców od dostarczane przez IBM-a czy HP. Nowy cykl za- – jakość aplikacji, jak i sterowników jest dla
programowania ekstremalnego i pragmatyczne- rządzania życiem produktu angażuje też w więk- nich niższa niż dla wersji 32-bitowych. Aby
go głosił m.in., że poprzez wytwarzanie oprogra- szym stopniu analityków biznesowych – prze- temu zaradzić, program certyfikacyjny, w
ramach którego producenci sprzętu i opro-
mowania oraz pomaganie innym w tym zakresie stają być już tylko twórcami dokumentacji, sta-
gramowania mogą uzyskać logo Compati-
odkrywamy lepsze sposoby realizowania tej pra- ją się decydentami, wpływającymi na ostatecz- ble with Windows 7, będzie wymagał pełnej
cy. W wyniku tych doświadczeń przedkładamy: ny kształt rozwiązania. West przedstawił final- zgodności produktów także z 64-bitowy-
jednostki i współdziałania między nimi nad pro- nie porady dla dużych firm, które by chciały za- mi wersjami Windows 7. Jak poinformował
cesy i narzędzia, działające oprogramowanie nad stosować Agile. Jego zdaniem, należy skupić się Relph, obecnie już ponad 6000 urządzeń i
aplikacji uzyskało logo zgodności. Ich listę
dokładną dokumentację, współpracę z klientem na tych procesach, które można wdrożyć już dzi-
można obejrzeć na stronie www.readyse-
nad negocjację umów, reagowanie na zmiany nad siaj. Niemal jednocześnie w Australii odbywała t7.com. Zawarte są tam też użyteczne wska-
realizowanie planu. Oznacza to, że wprawdzie się konferencja poświęcona Zwinnemu Progra- zówki dla deweloperów, które pomogą im
doceniamy to, co wymieniono po prawej stro- mowaniu. Wystąpił na niej John Dalton, głów- dostosować swoje produkty do wymogów
nie, to jednak bardziej cenimy to co wymieniono ny menedżer firmy Sensis. Stwierdził on, że choć jakości stawianych przez Redmond.
http:/windowsteamblog.com
po lewej. Podczas swojej sesji na wirtualnej konfe- korzyści z programowania Agile stają się coraz
rencji w Hewlett-Packard, starszy analityk Forre- bardziej wyraźne, to nie jest to coś, do czego moż- Wkrótce ostatnia
stera Dave West stwierdził, że w tradycyjnym ka- na podchodzić zbyt lekko. Jednym z problemów beta .NET 4 i Visual Studio 2010
skadowym podejściu do tworzenia oprogramo- jest wymóg współdziałania, który dla wielu pro- Microsoft udostępnił subskrybentom MSDN
wania istnieją wyizolowane obszary, w których gramistów jest wręcz nie do zniesienia. Przyzwy- wersję Beta 2 środowiska programistycz-
nego Visual Studio 2010 oraz platformy
realizuje się bardzo specjalistyczne zadania. W czajeni do pracy w izolacji, przed swoimi ekrana-
.NET Framework 4.0. Beta 2 prawdopodob-
miarę rozwoju firmy, podejście to prowadzi do mi, nagle muszą siedzieć w towarzystwie ubra- nie będzie już ostatnią przedpremierową
coraz większego skupiania się na przechodze- nych w garnitury i krawaty biznesmenów. Kło- wersją tej rodziny rozwiązań. Wydanie final-
niu od jednego etapu projektu do kolejnego, co poty sprawia też zmuszenie do współpracy lu- nej wersji planowane jest już bardzo kon-
prowadzi do wzrostu jego złożoności. Efekt jest dzi biznesu. Przyzwyczajeni, że wszystkie trud- kretnie – na 22 marca przyszłego roku. Beta
2 oczywiście nie wnosi praktycznie żadnych
przykry – projekt staje się coraz mniej zrozumia- ne kwestie zrzucają na głowy specjalistów od
nowych funkcji, programiści Microsoftu sku-
ły. W sytuacjach, gdy brakuje zrozumienia, odkry- technologii, nagle odkrywają, że nie mogą po- pili się teraz na poprawianiu błędów oraz na
wamy, że procesy stają się coraz bardziej skompliko- zbyć się całej odpowiedzialności. Oznacza to, że podwyższaniu wydajności oraz jakości pro-
wane, a to oznacza, że planowanie nie jest możliwe. menedżerowie średniego szczebla, muszą znacz- duktów. Przy okazji komunikatu o wersji
Nie możemy w pewnym momencie powiedzieć, żeby nie bardziej angażować się we współpracę z dzia- Beta 2 pojawiły się informacje o edycjach i
cenach nowego Visual Studio 2010. Okazuje
Bob zrobił X, ponieważ nie wiemy czym jest X i nie łami IT niż niegdyś. Jeśli zaś taki menedżer nie
się, że Microsoft ogranicza liczbę edycji pro-
wiemy czy Bob nadaje się do zrobienia tego, ponie- jest zbyt kompetentny w tym co robi, może się duktu do trzech – Professional (orientacyjna
waż za mało wiemy o całym problemie – objaśnił on okazać niezwykle szkodliwy dla całego pro- cena to 799 dolarów bez MSDN i 1199 dola-
West. Zdaniem Westa, Zwinne Programowa- cesu. Niemniej Dalton przyznał, że w wielu sy- rów z subskrypcją), Premium (5469 dolarów)
nie jest tym, co może uratować firmy przed za- tuacjach Agile jest niezastąpione. Wspomniał o oraz Ultimate (11,924 dolarów). Wersja Ulti-
mate zawierać będzie wszystkie funkcje
gubieniem się w rosnącej złożoności oprogramo- historii firmy Lonely Planet, w której pracował
znane obecnie z pakietu Team System, a
wania. Nie jest to tylko czcza teza – analityk For- jako szef działu IT. Objął tę pracę w momencie, wszystkie edycje poza najtańszą Professio-
restera przedstawił dokumentujące to statystyki. gdy firma przechodziła reorganizację, od niewiel- nal standardowo sprzedawane będą z sub-
W pierwszym kwartale 2009 roku Forrester py- kiego wydawcy książek do dużej firmy z branży skrypcją MSDN. Subskrybenci będą otrzy-
tał badane firmy software'owe o używane me- turystycznej. Próba stworzenia systemu infor- mywać za darmo prawo użytkowania Visual
Studio Team Foundation Server 2010, okre-
todologie programowania. 30% respondentów matycznego w modelu kaskadowym okazała się
śloną liczbę godzin obliczeniowych na plat-
stwierdziło, że korzysta z Agile. Podobne bada- sromotną porażką. Dzięki zastosowaniu praktyk formie Windows Azure i do 40 godzin szko-
nie przeprowadzone dwa lata temu pokazało, że Agile, które poznał w poprzedniej firmie, Dalton leń on-line rocznie. Warto także wspomnieć,
z praktyk Agile korzystało od 8 do 10% respon- zdołał szybko przygotować całą niezbędną infra- że każdy aktywny subskrybent MSDN na
dentów. West korzystając z tych wyników wyja- strukturę Lonely Planet. poziomie Premium w momencie premiery
Visual Studio 2010 zostanie automatycznie
śnia, że można dostrzec zmianę w trajektorii roz- http://itwire.com
przeniesiony do wyższej edycji.
woju firm. Model specjalizacji pracy zostaje coraz http://internetnews.com http://news.zdnet.co.uk/

www.sdjournal.org 7
Aktualności

Moblin: intelowska
wizja telefonu komórkowego Gartner: za trzy lata
Intel zaprezentował na Intel Develo-
per Forum 2009 nową wersję interfejsu Android numerem drugim
na rynku smartfonów
mobilnego – Moblin 2.1. Interfejs przy-
gotowany został z myślą o nowej gene-
racji urządzeń, które wykorzystując pro-

C
cesory Intel Atom, będą zarówno wydaj- hoć obecnie Android kontroluje za- telefonowi G1, było przyjęte raczej chłodno, to
nymi komputerami, jak i wszechstron- ledwie dwa procent rynku smartfo- Android 1.5 Cupcake był już całkiem dobrze
nymi smartfonami.
Moblin jest mobilną dystrybucją Linuk- nów, to jego perspektywy wzrostu są przemyślany. Na korzyść Androida ma też dzia-
sa, pierwotnie przygotowywaną z myślą znakomite – przynajmniej według prognoz Ke- łać sklep z aplikacjami i łatwe do wykorzysta-
o netbookach i nettopach (miniatu- na Dulaneya, analityka z firmy badawczej Gart- nia środowisko deweloperskie. Przygotowywa-
rowe komputery biurkowe). Wraz z ner. Jego zdaniem do 2012 roku mobilny sys- ne produkty dla konsumentów i biznesu powin-
nową wersją oprogramowania, system tem operacyjny Google'a będzie na tym ryn- ny zrobić z Androida dominującą platformę na
zaczyna być też adresowany do użyt-
kowników urządzeń typu MID – sto- ku numerem drugim. Dulaney przewiduje, że rynku mobilnym – stwierdził Dulaney. Jest to
jących na granicy między smartfona- przez najbliższe lata udział Androida w rynku możliwe tak dzięki otwartości tego systemu,
mi i netbookami/UMPC, wyposażony- wzrośnie siedmiokrotnie – podobnie jak to się jak i szerokiemu zakresowi urządzeń, na któ-
mi w funkcje telefoniczne. Intel planu- stało z iPhonem podczas jego dynamicznego rych będzie on działał. iPhoneOS można uru-
je zastosować telefoniczną wersję syste- okresu wzrostu w 2008 roku. Jednak iPhone chomić jedynie na telefonach od Apple'a, tym-
mu Moblin do obsługi platformy Moore-
stown, z procesorami Atom wykonany- nasycił już swoją niszę – tańszy i bardziej róż- czasem według analityka, w 2010 roku na ryn-
mi w technologii 45nm. Platforma norodny Android może dotrzeć do większej ku będzie około 40 modeli telefonów z Andro-
zakłada sprzętową akcelerację kodowa- liczby osób. Analityk Gartnera uważa, że nu- idem, produkowanych przez największe, od-
nia i dekodowania wideo oraz oferuje merem jeden na rynku smartfonów pozostanie noszące sukcesy firmy, takie jak Motorola czy
wsparcie dla łączności Bluetooth, HSPA, Nokia ze swoim Symbianem, utrzymując wciąż HTC. A czemu producenci tak chętnie wybie-
WiMAX i WiFi, nie zabraknie też pozycjo-
nowania przy użyciu GPS-u czy nawet około 39% rynku. Android uzyska 14,5%, iPho- rają Androida? Dulaney uważa, że zasługą te-
mobilnej telewizji. ne OS utrzyma się na poziomie nieco poniżej go jest dobre wyważenie funkcji systemu. Wy-
http://www.telepolis.pl 14%, kolejne miejsca zajmą Windows Mobile jaśnia to następująco: telefony z Symbianem
oraz BlackBerry OS (RIM), kontrolując nieco i Windows Mobile są skoncentrowane zdecydo-
Microsoft i Intel
ponad 12% rynku, a obecne miejsce Androida wanie na funkcjach komunikacyjnych urządze-
wprowadzą Silverlighta do Moblina
Microsoft nie ustaje w wysiłkach zmie- zajmie PalmPre, z udziałem na poziomie nie- nia, podczas gdy iPhone koncentruje się przede
rzających do popularyzacji platfor- co ponad 2%. Jak zatem widać, najwięcej stra- wszystkim na aplikacjach. Tymczasem Andro-
my Silverlight – obecnie najmocniej- ci na Androidzie Nokia – której Symbian jest id bardzo dobrze połączył te obszary, pozwala-
szego konkurenta Flasha firmy Adobe. obecnie instalowany na co drugim sprzedawa- jąc użytkownikom na łatwe wykonywanie czę-
Najnowszy pomysł Redmond to przy- nym smartfonie. Zdaniem Kena Dulaneya, fiń- stych operacji, bez konieczności przedzierania
gotowanie implementacji dla stworzo-
nej przez Intela wersji Linuksa dla urzą- ska firma będzie tracić przede wszystkim naj- się przez złożone menu i przełączania między
dzeń mobilnych: Moblina. Amerykańska bardziej zaawansowanych użytkowników, któ- aplikacjami. Idealną implementacją tej kon-
korporacja porozumiała się z Intelem w rzy wybiorą Androida ze względu na jego inte- cepcji ma być Cliq Motoroli z interfejsem Mo-
sprawie wprowadzenia Silverlighta do grację z usługami Google'a i innowacyjne apli- toblur, który bardzo efektywnie radzi sobie
systemów operacyjnych bazujących na kacje. Analityk jest przekonany, że system mo- z czynnościami komunikacyjnymi.
procesorach Atom. Przede wszystkim
chodzi tu właśnie o platformę Moblin. bilny Google'a rozwija się pod względem tech- http://webhosting.pl
Intel oficjalnie poinformował, że Silver- nicznym w szybkim tempie – o ile jego pierw- http://heise.de
light będzie działał w Moblinie już na sze wydanie, towarzyszące nieudanemu raczej http://techworld.com
początku 2010 roku. Pozwoli to na uru-
chamianie interaktywnych aplikacji

Ballmer: Microsoft
webowych w netbookach, smartfonach,
a nawet w komputerach pokładowych
samochodów.
Microsoftowi zależy na tym, aby jego
platforma nie była ograniczona wyłącz- nie pozbierał się po Viście
S
nie do desktopów i laptopów klasy teve Ballmer, prezes firmy Microsoft, ofi- tacja jego firmy poprawi się wraz z wydaniem
PC. Flash zagarnia ponad 95% tego
rynku i pokonanie go konwencjonalny- cjalnie przyznał, że wydanie Visty źle od- Windows 7, do którego uaktualnienie z Visty
mi metodami jest po prostu niemożli- biło się na dobrym imieniu korporacji i powinno być w przeważającej większości przy-
we. Redmond chce więc znaleźć niszę, że firmie nigdy później nie udało się już zatrzeć padków bezbolesne i nie wymaga zmian sprzę-
w której wyprzedzi Adobe. Warto rów- złego wrażenia powstałego po premierze syste- towych. Głosy z Microsoftu mówiące o tym, że
nież pamiętać, że inżynierowie Novel- mu. System Windows Vista pojawił się na rynku Vista jest produktem nieudanym, słychać już od
la stworzyli otwartą wersję platformy
Silverlight o nazwie Moonlight. Będzie w 2007 roku. Od początku krytykowany był on jakiegoś czasu. Teraz oficjalnie firma uznała ak-
ona wykorzystywana w klasycznych m.in. za niską wydajność. Według Ballmera, Mi- tualnie jeszcze najnowszy na rynku system za
dystrybucjach Linuksa. Od 22 wrze- crosoft popełnił błąd podczas projektowania Vi- niewypał. Konto jest więc czyste i przygotowa-
śnia br. Silverlight jest również oficjal- sty polegający na zwiększeniu bezpieczeństwa ne na przyjęcie Windows 7, który już niebawem
nie obsługiwany przez system Windows systemu kosztem zmniejszenia kompatybilno- zajmie miejsce Visty na sklepowych półkach.
Embedded CE.
http://webhosting.pl ści. Prezes Microsoftu wierzy jednak, że repu- http://www.telegraph.co.uk/

8 12/2009
Aktualności

Microsoft Testowa wersja


Google Web Toolkit 2.0

Technology Summit 2009 Popularny webowy framework Google


Web Toolkit jest już dostępny w wersji
2.0. Niestety trochę jeszcze czasu minie,

P
od koniec września br. odbyła się zanim będzie się nadawał do zastoso-
czwarta edycja Microsoft Techno- wań produkcyjnych – wydana wersja
logy Summit (MTS). Przez dwa dni została oznaczona jako Milestone 1, zaś
3000 uczestników konferencji miało oka- jej twórcy ostrzegają, że jest pełna uste-
rek. Jednak zainteresowani GWT już teraz
zję zapoznać się z najnowszymi produk- dzięki niej mogą zapoznać się z najważ-
tami Microsoft, jak również wymienić się niejszymi innowacjami. Google Web Tool-
opiniami z profesjonalistami oraz mówca- kit to de facto skrośny kompilator Javy do
mi na temat najważniejszych wyzwań sto- wienie tych aplikacji wchodzących w skład JavaScriptu, który pozwala na tworzenie
jących przed rynkiem IT. Windows 7, które czynią go wyjątkowym AJAX-owych aplikacji, nie wymagając od
dewelopera jakiejkolwiek znajomości tej
Microsoft jest obecnie firmą, która na ba- rozwiązaniem. technologii. Po napisaniu kodu w Javie,
dania i rozwój przeznacza aż 9,1 miliarda Doceniają to pierwsi użytkownicy syste- GWT przetwarza część kliencką na Java-
dolarów, co plasuje nas na pierwszym miej- mu, których w Polsce jest już 300 tysięcy, Script, HTML i CSS, zaś część serwerowa
scu wśród wszystkich firm informatycznych choć do oficjalnej premiery pozostało jesz- zostaje skompilowana do serwletu Javy,
na świecie – powiedział Jacek Murawski, cze kilka tygodni. Uczestnicy konferen- gotowego do uruchomienia na serwe-
rze aplikacyjnym. Zaletą takiego rozwią-
dyrektor generalny Microsoft Polska. – cji mogli przekonać się, jakim udogodnie- zania jest znacznie łatwiejsze wykrywa-
Nasz przemysł dojrzał. Obecnie nie jest moż- niem w pracy z Windows 7 są takie roz- nie błędów, oraz uwolnienie się od zmory
liwe, aby kilku zapaleńców z setką dolarów wiązania jak Problem Steps Recorder, Win- dopasowywania aplikacji do poszczegól-
każdy mogło dokonać przełomowego odkry- dows Troubleshooting Platform, tryb Win- nych przeglądarek – o wszystko to dba
cia w IT. Warto zatem stawiać na rozwiąza- dows XP czy też BitLocker to Go. sam framework. Deweloperzy GWT zde-
cydowali się zmienić w wersji 2 stosowa-
nia oferowane przez duże firmy, które nie bo- Nie mniej emocjonująca była prezenta- ną terminologię. Tryb hostowany to teraz
ją się inwestować w badania i rozwój. A Mi- cja Windows Server 2008 R2, który swo- tryb deweloperski, a tryb webowy to tryb
crosoft się nie boi, czego dowodem są prezen- je największe możliwości odkrywa w połą- produkcyjny. Nowe GWT wykorzystuje w
towane na MTS rozwiązania. Udowodniły czeniu z Windows 7. Jedną z takich funk- trybie deweloperskim system wtyczek,
to już pierwsze sesje konferencji, podczas cji będzie DirectAccess. Ma ona umożli- dzięki czemu ten sam plik instalacyjny
może być używany na dowolnej platfor-
których przedpremierowo publicznie za- wiać m.in. zdalny, bezpieczny dostęp do mie. Wcześniej debugowanie odbywało
prezentowano Windows 7 oraz Windows zasobów sieci korporacyjnej spoza firmo- się w specjalnej hostowanej przeglądarce,
Server 2008 R2. Były to najważniejsze se- wego środowiska ITi bez wykorzystania teraz będzie to możliwe w normalnych
sje tegorocznego MTS. Windows 7 to pięk- połączeń tunelowych VPN. Wydarzeniem przeglądarkach – Chrome, Firefoksie
ny mechanizm – powiedział Patryk Góra- drugiego dnia MTS 2009 była możliwość czy Internet Explorerze. Wystarczy tylko
wybrać odpowiednią dla danego brow-
lowski z polskiego oddziału Microsoft pod- przetestowania komputera Surface, na sera wtyczkę. Pojawiła się opcja podzie-
czas prezentacji. – Nowy Windows powstał którym jednocześnie może pracować kil- lenia kodu na wiele fragmentów, co ma
w oparciu o wyniki analizy opinii użytkow- ku użytkowników i to bez pomocy mysz- przyspieszyć uruchamianie aplikacji, oraz
ników Windows Feedback Program, który jest ki czy klawiatury. Wokół niepozornie wy- deklaratywnego budowania interfejsów
dostępny od momentu pojawienia się na ryn- glądającego urządzenia gromadzili się pa- użytkownika za pomocą XML-a. Wprowa-
dzono także moduł HtmlUnit do przepro-
ku Windows Vista. Dzięki tej analizie został sjonaci IT, którzy na własnej skórze chcie- wadzania testów, który jest napisany cał-
stworzony mechanizm, który jest prosty, moc- li się przekonać, czy to możliwe. Surface kowicie w Javie. GWT 2.0 przynosi także
ny i elegancki. Prosty w obsłudze i zrozumie- przypomina stolik do kawy, którego blat pakietowanie zasobów (ClientBundle).
niu przez użytkownika. Mocny, ponieważ jest jest jednocześnie ekranem oraz interfej- W wersji 1.4 frameworka wprowadzono
dobrze zorganizowany wewnętrznie i jest w sem komputera. mechanizm ImageBundles, który pozwa-
lał na automatyczne tworzenie sprite'ów
stanie wykonać wiele różnych zadań jedno- Podczas MTS 2009 zaprezentowano z obrazków. ClientBundle uogólnia tę
cześnie. Elegancki, ponieważ jest doskona- wiele technologii Microsoft, w tym Sea technikę na wszelkie zasoby, pozwalając
le zaprojektowany i posiada najlepsze cechy Dragon do szybkiej obsługi informacji łączyć w jednym pliku wszelkie zasoby –
swojego gatunku. optycznej, Windows Mobile 6.5 czy Silver- w tym pliki tekstowe, XML-owe czy arku-
Prezentacja Windows 7 poświęcona by- light 3. Program konferencji oraz jej prze- sze CSS. Pozwala to na znaczne ograni-
czenie liczby połączeń sieciowych gene-
ła siedmiu obszarom, które cechują no- bieg, poziom seminariów oraz poszcze- rowanych przez aplikację – co ważne
wy system operacyjny, czyli: prostota ob- gólnych warsztatów został bardzo dobrze jest szczególnie dla aplikacji mobilnych.
sługi, skalowalność do potrzeb użytkow- przyjęty przez 3000 zarejestrowanych Dzięki wprowadzonym w architektu-
nika, bezpieczeństwo użytkownika i jego uczestników MTS, którzy już teraz czeka- rze zmianom, GWT 2.0 jest dostarczany
zasobów zapisanych w komputerze, mul- ją na jego kolejną edycję. w tym samym pliku dla trzech najważ-
niejszych systemów – Linuksa, Mac OS
timedialność, wydajność oraz rozwiązania http://webhosting.pl X-a i Windows. Całość, wydaną na wolnej
o charakterze pro-ekologicznym, wspiera- licencji Apache, można pobrać tutaj.
nie użytkownika w najważniejszych mo- Twórcy projektu proszą jego użytkowni-
mentach pracy na komputerze, współpraca ków o zgłaszanie wszelkich napotkanych
z innymi urządzeniami i starszymi progra- problemów.
http://webhosting.pl
mami. Każdy z obszarów obejmował omó- http://code.google.com

www.sdjournal.org 9
Aktualności

Google przepłaciło
za YouTube około miliard dolarów... Flash Player 10.1
5
Gdy w 2006 roku firma Google kupiła YouTu- października br. na konferencji Adobe elementów ich interfejsu. Flash 10.1 ma stać się
be, wielu specjalistów zastanawiało się, co
MAX 2009 przedstawiony został ofi- także domyślną w branży technologią dostarcza-
dokładnie skłoniło internetowego giganta
do wyłożenia tak dużej ilości pieniędzy (1,65 cjalnie Flash Player 10.1. Nowa wer- nia mediów poprzez streaming HTTP. Zintegro-
miliarda dolarów). Nie jest bowiem tajemni- sja odtwarzacza trafi jednak nie tylko na desk- wany mechanizm ochrony treści przed pirac-
cą, że najpopularniejszy obecnie serwis fil- topy – jej głównym celem są urządzenia mobil- twem – Adobe Flash Access 2.0 – ma sprawić,
mowy nie jest na tyle dochodowym bizne- ne. Umożliwia odtworzenie wszelkich mediów że dostawcy treści będą mogli zaoferować śmia-
sem, by można było w rozsądnym czasie
i uruchomienie każdej aplikacji Flash wszystkim ło w Sieci swoje produkcje, bez obawy, że po kil-
odzyskać włożone w niego środki. Wiele w
całej sprawie wyjaśniają stenogramy rozpra- smartfonom (a więc korzystającym z Symbia- ku godzinach pojawią się w serwisach z torren-
wy sądowej i zeznania Erica Schmidta, pre- na, Androida, Windows Mobile, webOS-a czy tami. Co ciekawe, wydanie Flasha 10.1 spotka-
zesa Google. Wiele wskazuje na to, że Eric BlackBerry OS-a) poza iPhonem Apple'a. We- ło się z aprobatą Microsoftu, próbującego ugrać
Schmidt gotów był zapłacić za 18-miesięczną dług Adriana Ludwiga, menedżera marketin- choć małą część rynku dla swojego Silverlighta.
firmę tak dużo po prostu po to, by mieć pew-
gu platformy Flash, już wkrótce udostępniona Stephanie Ferguson, główny menedżer zarzą-
ność, że ją kupi. Oferta musiała być bowiem
na tyle atrakcyjna, by skutecznie zniechęcić będzie deweloperska wersja beta dla Windows, dzania produktem w Redmond, stwierdziła, że
potencjalnych konkurentów (m.in. Yahoo! czy Mac OS X-a i Linuksa oraz Windows Mobile, technologia Adobe Flash będzie kluczowym elemen-
Microsoft). Prezes Google przedstawił więc zaś implementacje dla Androida i Symbiana po- tem nowych telefonów z Windows, pozwalającym
zarządowi spółki z Mountain View swoje wyli- jawią się na początku 2010 roku. Wersja stabilna ludziom na cieszenie się atrakcyjnymi grami, filma-
czenia dotyczące faktycznej ceny YouTube
10.1 ma pojawić się w pierwszej połowie 2010 mi i interaktywnymi aplikacjami WWW wszę-
(około 600 milionów dolarów) oraz doliczył
do tego bonus, jaki był niezbędny do tego, roku. Jednocześnie Adobe i RIM, producent po- dzie tam, gdzie się udadzą. Nowa wersja Flasha
by mieć pewność co do kupna. Propozycja pularnych w USA smartfonów BlackBerry, za- jest skazana na sukces. W ramach Open Screen
została oczywiście zaakceptowana. YouTube powiedzieli współpracę nad przygotowaniem Project działa już 50 największych firm z branży
to spółka z bardzo niskimi dochodami. Jed- wersji Flasha także dla tych urządzeń. W ten mobilnej i IT, w tym od niedawna Google. Pene-
nakże rozwijała się ona niezwykle szybko,
sposób Flash stanie się wszechobecny – jedynie tracja rynku desktopów przez technologię Flash
znacznie szybciej niż Google Video. Co więcej,
krążyły pogłoski o tym, że ma zostać sprzeda- iPhone'y będą go pozbawione. Nowa wersja śro- przekroczyła 98%, zaś 40% sprzedawanych te-
na. Doszliśmy więc do wniosku, że kwota 1,65 dowiska uruchomieniowego Adobe wprowadza lefonów zawiera odtwarzacz Flash Lite. Ponad
miliarda dolarów da nam pewność, że sta- technologie opracowane w ramach Open Screen 70% gier webowych i 75% wideo jest przygoto-
niemy się współautorami ogromnego suk- Project, co według Adobe pozwoli na wyświe- wywanych z wykorzystaniem technologii Ado-
cesu YouTube w przyszłości – stwierdził Eric
tlanie w przeglądarkach zaawansowanych apli- be. Porozumienia, które firma ta zawarła z pro-
Schmidt, odpowiadając na pytania prawnika
Viacom podczas rozprawy sądowej dotyczą- kacji i mediów wysokiej rozdzielczości. Two- ducentami sprzętu, mogą tylko zwiększyć po-
cej pogwałcenia praw autorskich przez ten rzenie aplikacj na tę platformę ma być łatwiej- pularność Flasha. Więcej o nowej wersji odtwa-
popularny serwis filmowy. Dziś trudno się nie sze dzięki nowemu modelowi programistycz- rzacza Adobe'a można dowiedzieć się na stro-
zgodzić ze słowami prezesa Google. W maju nemu, pozwalającemu na łatwe wykorzysta- nie labs.adobe.com/technologies/flashplayer10/.
2006 roku YouTube miał jedynie 12 milionów
nie fragmentów istniejącego kodu. Najważniej- Z kolei na stronie labs.adobe.com/technologies/
unikalnych użytkowników. Dziś, tylko w Sta-
nach Zjednoczonych, liczba ta przekroczyła szą jednak chyba innowacją w 10.1 jest wpro- flashplayer10/videos/ dostępne są filmy przedsta-
już 100 milionów. Co więcej, ówczesną decy- wadzenie obsługi sprzętowej akceleracji grafi- wiające możliwości Flasha 10.1 na smartfonach
zję Erica Schmidta popierają liczni specjali- ki, zarówno 2D, jak i 3D oraz strumieni wideo. PalmPre i urządzeniach z Windows Mobile.
ści, wśród nich James McQuivey z Forrester Z kolei na urządzeniach mobilnych możliwe bę- http://labs.adobe.com
Research. Jego zdaniem, wykupienie YouTu-
dzie korzystanie w aplikacjach z ekranów doty- http://eweek.com
be miało dla Google wiele dodatkowych
plusów, m.in. fakt, iż największa strona tego kowych, akcelerometrów i innych specyficznych
typu będzie należeć do tej właśnie spółki, a
nie do konkurencji. Jeśli nawet nie uda się
odzyskać włożonych pieniędzy, to i tak, zda- Apple skorzysta
na wydaniu Windows 7?
niem analityka, cała transakcja była opłacal-
na. Z takim twierdzeniem nie zgadza się inny
znawca rynku, Josh Martin z Yankee Group

O
Research. Uważa on, iż za tak ogromnym czywistym jest, że Microsoft liczy jego systemu operacyjnego dołącza darmowy
sukcesem YouTube stało to, że użytkownicy na duży sukces po wydaniu Win- pakiet aplikacji iLife, podczas gdy użytkowni-
tego serwisu mogli zdobyć dostęp do rzeczy dowsa 7. Wygląda jednak na to, że cy Windowsa muszą sami zaopatrzyć się w po-
umieszczonych tam w sposób mogący naru-
na to samo liczy... Apple. Użytkownicy zaczy- trzebne oprogramowanie. Wszystko to sprowa-
szać prawa autorskie. W przypadku gdyby
serwis ten oferował jedynie w pełni legal- nają być zmęczeni Windowsem i bólami gło- dza się do tego, że paradoksalnie Apple może
ną zawartość, jego popularność mogła- wy, które wywołuje. Widzieliśmy to przy pre- bardziej skorzystać na premierze Windows 7,
by być znacznie niższa. Zgodnie z tą teorią, mierze Visty, XP i innych systemów w przeszło- niż sam Microsoft. Apple ponadto zaprzecza,
taki spadek popularności jest więc możli- ści. Według Crolla, czarę goryczy w użytkowni- jakoby miało planować obniżki cen, kampanię
wy w przyszłości. Spory więc trwają, a roz-
kach systemów Microsoftu ma przepełnić ilość reklamową lub inne wydarzenia w odpowiedzi
strzygnięcie który z głosów jest głosem biz-
nesowego rozsądku jest obecnie niezwykle pracy, jaką muszą wykonać, aby zamienić Win- na wydanie Windows 7. Może to być związane
trudne, jeśli nie niemożliwe. Czas pokaże, czy dowsa XP na najnowszy Windows 7. Ludzie są z analizą przeprowadzoną przez Briana Marshal-
szefowie Google w kolejnych latach wciąż tak tym zmęczeni i to jest kolejny świetny pretekst, la z firmy Broadpoint.Gleacher, według którego
przychylnie będą się wypowiadać o jednej ze by sprawdzić Maca – mówi. Kolejną kwestią jest Windows 7 nie zagrozi pozycji Apple na rynku.
swoich największych inwestycji.
koszt oprogramowania. Apple do każdego swo- http://www.cnet.com/
http://www.cnet.com/

10 12/2009
Aktualności

Czy ORM-y Lucene 2.9: wyszukiwanie


prawie w czasie rzeczywistym

odejdą wkrótce do lamusa? Znaczący skok w numeracji wersji pocią-


gnął za sobą znaczące zmiany w popular-
nej bibliotece Lucene. Koszt ich wprowa-

S
tephen Schmidt, znany w środowi- problemu niezgodności impedancji, lecz dzenia jest jednak dość wysoki: zerwanie
sku programistów Javy autor blo- tylko odsuwają go w czasie. Schmidt uwa- pełnej kompatybilności ze starszymi wersja-
ga Code Monkeyism, postawił bar- ża, że choć programistom się wydaje, że mi. Apache Lucene to wolna i otwarta biblio-
dzo kontrowersyjną tezę, która może nie ORM-y działają, to wcale tak nie jest. Cią- teka Javy, która pozwala na efektywne indek-
sowanie i przeszukiwanie informacji teksto-
spodobać się wielu osobom. Jego zdaniem gle rzucają wyjątki. Według niego, w Hi- wych, niezależnie od ich formatu. Stanowi
mapowanie obiektowo-relacyjne (ORM) bernate nieustannie ma się do czynienia bazę dla licznych wyszukiwarek interneto-
wkrótce odejdzie do lamusa, po części za z wyjątkami inicjalizacji w najbardziej nie- wych, wykorzystywana jest także przez więk-
sprawą coraz silniejszego ruchu NoSQL. szczęśliwych okolicznościach tylko dlate- sze witryny do realizacji lokalnych wyszuki-
Jak wiadomo, mapowanie obiektowo-rela- go, że programista poczynił złe założenia wań. Po ponad pół roku prac od wydania
wersji 2.4.1, rozwijający Lucene zespół dewe-
cyjne to odwzorowanie obiektowej archi- co do sesji i zakresu. Takie problemy trud- loperski wydał wersję 2.9 – jest ona przy-
tektury systemu na bazę danych o charak- no nie tylko naprawić, ale w ogóle je zlo- gotowaniem do całkowicie odświeżonego
terze relacyjnym. Prowadzi to do powsta- kalizować. wydania Lucene 3.0, które będzie wymagał
nia wirtualnej obiektowej bazy, do której Może jeszcze wyżej opisane problemy Javy 1.5 (obecnie projekt działa jeszcze z Javą
można uzyskać dostęp z logiki bizneso- nie byłyby zabójcze dla ORM-ów, ale po- 1.4). W odsłonie 2.9 wprowadzono około
setki zmian względem poprzedniej wersji,
wej aplikacji. jawienie się NoSQL-owych hurtowni da- z czego 36 to nowe elementy funkcjonal-
Na rynku dostępnych jest wiele goto- nych marginalizuje w programowaniu dla ności. Wśród tych ostatnich warto wymie-
wych narzędzi do tego – np. Hiberna- dużego biznesu wykorzystanie mapowa- nić wprowadzenie do IndexWritera wyszuki-
te dla Javy, ADO.NET Entity Framework nia obiektowo-relacyjnego. Jest to efektem wania prawie w czasie rzeczywistym, odręb-
dla języków Microsoftu czy SQLAlchemy wykorzystania coraz większej liczby języ- nych wyszukiwań i cache'owania na każdy
segment, nowych typów kwerend, ulepszo-
dla Pythona. Jeszcze na początku wieku ków w biznesie – współczesny programi- nych wieloznaczników, lepszej obsługi Uni-
ORM-y niepodzielnie królowały w świecie sta staje się poliglotą, który wykorzystuje code, wysokowydajnej obsługi pól nume-
korporacyjnego oprogramowania. Sam au- takie akurat narzędzia, jakie najlepiej na- rycznych, nowego silnika parsowania oraz
tor kontrowersyjnej tezy o ich zmierzchu dają się do rozwiązania problemu. W tej nowych analizatorów dla języków Wscho-
wyjaśnia, że jeszcze pod koniec ubiegłego sytuacji wprowadzanie danych do hurtow- du: arabskiego, perskiego i mandaryńskiego.
Deweloperzy Lucene zalecają, aby nie prze-
stulecia napisał kilka własnych ORM-ów. ni poprzez ORM-y staje się znacznie trud- nosić na siłę starych aplikacji na Lucene 2.9,
Jednak dzisiaj, zdaniem Schmidta, ORM- niejsze. Schmidt proponuje, aby przy roz- ale przebudować je od podstaw, głównie ze
y to ułuda. Ich celem miało być szybkie poczęciu pracy nad nowym projektem za- względu na wspomniane wcześniej zerwa-
tworzenie aplikacji i uniknięcie pisania trzymać się i zastanowić, czy napraw- nie kompatybilności z poprzednimi wersja-
wielokrotnie powtarzalnego kodu. Jed- dę potrzebujemy ORM-a. Cache'owanie mi biblioteki.
http://apache.org
nak przynoszą one problemy, które znacz- w XML-u czy JSON jest efektywniejsze niż
nie przerastają oferowane przez nie korzy- cache'owanie obiektowe, za pomocą które- Nowy Google AdSense:
ści. Główny problem to wydajność – nikt go można nieco przyśpieszyć ORM-y. A je- większe reklamy na komórkach
nie chce zaglądać do logów Hibernate SQL śli już potrzeba prawdziwego cache'owania Nowa generacja reklam w każdej chwili może
z tego powodu, pisze Schmidt, ponieważ obiektowego, to Schmidt poleca zaintere- zawitać na nasze komórki. Wszystko przez
zmiany, jakie Google wprowadził do projek-
gdyby zobaczyć na własne oczy kwerendy sować się takimi rozwiązaniami jak ehCa- tu AdSense. Do tej pory ten serwis reklamo-
przez ten ORM generowane, to mogłoby che czy Terracota. wy był w stanie wyświetlać jedynie niewiel-
się to źle skończyć. Z kolei aby uniknąć wielokrotnego pi- kie, proste reklamy za pośrednictwem stron
Jak napisał niedawno na swoim blogu sania tego samego kodu, warto sięgnąć do internetowych przeznaczonych na tele-
niejaki Aldo z Nowej Zelandii, głównym Data Access Object, komponentów, które fony komórkowe. Wszystko zawierało się
w tekście lub obrazku o niewielkich rozmia-
powodem dla którego się korzysta z ORM- dostarczają jednolitego interfejsu do ko- rach. Google postanowiło iść krok naprzód,
ów, jest magiczna fraza niezgodność impe- munikacji między aplikacją a bazą danych wykorzystując duży potencjał nowocze-
dancji (ang. impedancy mismatch), za co i mają dobrą implementację CRUD (Create snych smartphonów. Nowy AdSense wyko-
winę ponosi sama koncepcja języka zapy- Retrieve Update Delete). Tutaj Schmidt po- rzystuje zmodyfikowany kod JavaScript,
tań, zakładająca brak algorytmicznej uni- leca np. Spring JDBC i język Scala. Dobre który został zoptymalizowany dla współcze-
snych urządzeń mobilnych obsługujących
wersalności. DAO, zdaniem blogera, dostarcza wszyst- w pełni przeglądarki HTML. Nowy serwis
Jednak czy takie magiczne myślenie ma kich niezbędnych operacji bazodanowych reklamowy umożliwia wyświetlanie nowo-
sens? Aldo pisze, że zależy to od tego, w ja- przy znacznie mniejszej ilości czarowania, czesnych reklam w formatach odpowiada-
ki sposób i jak często abstrakcje nawala- niż robią to ORM-y. jących najpopularniejszym rozmiarom ekra-
ją – a w wypadku ORM-ów dzieje się to http://webhosting.pl nów sprzedawanych dziś smartphonów.
Google zapewnia, że nowa usługa została
nader często. Można wykorzystać ORM-a http://codemonkeyism.com zoptymalizowana tak, by nie wymagała od
i myśleć na poziomie obiektowym, ale kie- http://hatfulofhollow.com użytkowników ściągania zbyt dużej ilości
dy trzeba zrobić cokolwiek bardziej skom- http://nearinfinity.com danych. W najbliższym czasie więc użytkow-
plikowanego, np. zoptymalizować kweren- nicy smartphonów spodziewać się mogą
dę, to znów wracamy do krainy tabel i klu- większych i, na niektórych witrynach, zapew-
ne bardziej natarczywych reklam...
czy zewnętrznych. ORM-y nie rozwiązują http://www.dobreprogramy.pl

www.sdjournal.org 11
Biblioteka miesiąca

Google
Collections Library
Eleganckie i efektywne kolekcje w Javie
Kolekcje to nieodłączny element skrzynki narzędziowej każdego
programisty. Jeśli programujesz w Javie i chciałbyś uprościć oraz
zoptymalizować Twój kod odpowiedzialny za obsługę kolekcji, to
trafiłeś na właściwy artykuł. Zapraszam do lektury!
wanie) określa kontener podobny do Map (mapo-
Dowiesz się: Powinieneś wiedzieć: wanie) z JCF, z tą jednak różnicą, że potrafi on
• Co oferuje biblioteka Google Collections Li- • Jak programować w języku Java. łączyć wiele wartości z jednym kluczem. Dla
brary i dlaczego warto jej używać. • Solidne podstawy biblioteki Java Collection przykładu, jeśli użytkownik biblioteki dwu-
• Jak można ją wykorzystać w praktyce. Framework. krotnie wywoła metodę put(K, V) na konte-
nerze typu Multimap, przy czym klucz (K) za
każdym razem będzie taki sam, zaś wartości
Przegląd możliwości (V) będą różne, to w rezultacie kontener bę-
Pisząc w największym możliwym skrócie, Go- dzie zawierał odwzorowania klucza K do oby-
Poziom ogle Collection Library to zestaw nowych in- dwu wartości. W przypadku kontenerów ty-
trudności terfejsów kolekcji, ich implementacji oraz klas pu Multimap metoda get() zwraca kolekcję za-
pomocniczych. GCL należy postrzegać jako wierającą wszystkie wartości powiązane z zada-
bibliotekę stanowiącą rozszerzenie Java Col- nym kluczem:
lections Framework. Poniżej przedstawio-

W
dzisiejszych czasach, kiedy progra- na jest lista najbardziej kluczowych składni- Collection<V> get(K key);
mowanie często polega na składa- ków GCL:
niu aplikacji z gotowych klocków, GCL dostarcza cały szereg implementacji in-
a budowane programy są coraz bardziej i bar- • Nowe interfejsy kolekcji: Multimap, terfejsu Multimap : ArrayListMultimap,
dziej złożone, zestaw podstawowych klas kolek- Multiset oraz BiMap. ForwardingMultimap, HashMultimap,
cji jest elementem absolutnie podstawowym i • Wysoko wydajne, niemodyfikowalne (ang. ImmutableListMultimap, ImmutableMultimap,
nieodzownym. Z tego względu kolekcje stały immutable) implementacje standardowych ImmutableSetMultimap, LinkedHashMultimap,
się integralnym składnikiem bibliotek standar- klas kolekcji JCF, np. ImmutableSet. LinkedListMultimap, TreeMultimap.
dowych nowoczesnych języków programowa- • Użytkowe klasy Lists, Sets oraz Maps, Interfejs Multiset (wielozbiór) definiuję ko-
nia. Java nie jest w tym przypadku wyjątkiem. wspomagające pracę ze standardowymi lekcję, która zachowuje się podobnie jak Set
Standardowy pakiet Java Collection Frame- kontenerami biblioteki JCF. (zbiór) z JCF, aczkolwiek pozwala na dupli-
work to solidny zestaw kontenerów ogólnego • Użytkowe klasy Iterators i Iterables kację elementów. Powtarzające się elementy
użytku i pomimo tego, że został on zaprojekto- zawierające zestawy statycznych metod wielozbioru, zwane są wystąpieniami zaś su-
wany i zaimplementowany kilkanaście lat temu operujących na interfejsach Iterator maryczna ich liczba określana jest jako licznik
– ciągle jest z powodzeniem stosowany. Jednak- i Iterable. wystąpień. Kontener typu Multiset jest w sta-
że, użytkownicy JCF mogą od czasu do cza- • Klasa Ordering, czyli znacznie ułatwiająca nie przechować maksymalnie Integer.MAX_
su odnieść wrażenie, że pewne operacje na ko- operacje sortowania obiektów według wie- VALUE wystąpień danego elementu. Interfejs
lekcjach można by wykonywać nieco prościej, lu, potencjalnie złożonych kryteriów. ten dostarcza oczywiście szereg odpowiednich
szybciej i efektywnej. Do tego samego wnio- • Szereg dodatkowych udogodnień związa- metod, np. count() zwracającą licznik wystą-
sku doszli dwaj pracownicy firmy Google: Ke- nych z obsługą kontenerów w języku Java. pień dla zadanego obiektu. GCL dostarcza na-
vin Bourrillion oraz Jared Levy. Co więcej, na stępujące implementacje interfejsu Multiset:
myśleniu się nie skończyło. W efekcie prac tych W kolejnych podpunktach przyjrzymy się ConcurrentHashMultiset, EnumMultiset,
dwóch panów powstała biblioteka Google Col- składnikom GCL wymienionym w pierw- ForwardingMultiset, HashMultiset,
lections Library, stanowiąca świetne uzupełnie- szych pięciu punktach powyższej listy. ImmutableMultiset, LinkedHashMultiset,
nie i rozszerzenie pakietu Java Collection Fra- TreeMultiset. Interfejs BiMap (mapowanie
mework. W niniejszym artykule przedstawię Multimap, Multiset oraz BiMap dwukierunkowe) definiuje kolekcję-mapowa-
pokrótce genezę tej biblioteki, opiszę jej pod- Multimap, Multiset i BiMap to nowe interfejsy nie, w które gwarantuje unikalność zarówno
stawowe składniki i pokażę jak można zastoso- kolekcji wprowadzone w ramach Google Col- kluczy jak i wartości. Dzięki temu mapowa-
wać ją w praktyce. lections Library. Interfejs Multimap (wielomapo- nie dwukierunkowe wspiera tzw. widok od-

12 12/2009
Google Collections Library

wrotny, w którym wartości mapowania stają samego klucza. Multiset to idealna kolekcja do mutable). Stałość obiektu to bardzo ważny idiom
się kluczami zaś klucze – wartościami: budowania histogramów, więc wykorzystałem programistyczny (bardzo ciekawa dyskusja na
ją w celu zliczania wystąpień znaków zawar- ten temat przedstawiona jest w świetnej książce
BiMap<V,K> inverse(); tych w argumentach przekazanych z linii pole- p.t. Effective Java, 2nd Edition). JCF udostępnia
ceń. BiMap z kolei został użyty jako słownik, któ- specjalne opakowania na kolekcje, które czynią je
GCL dostarcza następujące implementacje ry można odpytywać w dwie strony. niemodyfikowalnymi, aczkolwiek zarówno efek-
interfejsu BiMap : EnumBiMap, EnumHashBiMap, tywność jak i wygoda związana z ich używaniem
HashBiMap oraz ImmutableBiMap. Kolekcje niemodyfikowalne pozostawiają wiele do życzenia. GCL oferuje w
Na Listingu 1 przedstawione są przykłado- Często zdarza się, że wiemy z góry, jaka będzie za- zmian specjalizowane, niemodyfikowalne kolek-
we przypadki użycia przedstawionych powy- wartość danego kontenera (np. tuż po jego stwo- cje, które są znacznie prostsze w użyciu, szybsze
żej trzech nowych typów kolekcji. W przypad- rzeniu wypełniamy go specyficznym obiektami) i zużywają mniej pamięci. Listing 2 przedstawia
ku wielomapowania warto zwrócić uwagę na i chcielibyśmy aby reszta naszego programu trak- typowy przykłady wykorzystania niemodyfiko-
to, jak można dodawać różne wartości dla tego towała go jako byt niemodyfikowalny (ang. im- walnego zbioru liczb całkowitych w stylu JCF.
Kontrprzykład opary na GCF mieści się w
Listing 1. Proste przypadki użycia kontenerów Multimap, Multiset i BiMap dwóch linijkach kodu:

import com.google.common.collect.*; public static final ImmutableSet<Integer>


import java.util.List; MY_HAPPY_NUMBERS
public class GclListing1 { = ImmutableSet.of(0, 1, 3, 7, 13, 44);
public static void main(String[] args)
{ Komentarz porównujący te dwa fragmenty ko-
Multimap<String,Integer> mmap = ArrayListMultimap.create(); du wydaje się być zbyteczny... GCF udostępnia
mmap.put("odd", 1); cały szereg niemodyfikowalnych kontenerów,
mmap.put("odd", 3); pełna ich lista jest dostępna w dokumentacji
mmap.put("odd", 5); API biblioteki (patrz: ramka W sieci).
mmap.put("odd", 7);
mmap.put("even", 2); Klasy użytkowe:
mmap.put("even", 4); Lists, Sets i Maps
mmap.put("even", 6); Trzy użytkowe klasy wymienione w tytule niniej-
mmap.put("even", 8); szego podpunktu oferują zestawy statycznych
System.out.println(mmap); metod służących przede wszystkim do tworzenie
System.out.println(mmap.get("even")); i manipulacji obiektami kolekcji typu List, Set i
Multiset<Character> histogram = HashMultiset.create(); Map. Przeznaczenie tych klas najłatwiej będzie po-
for(String arg : args) kazać na praktycznym przykładzie. Spójrzmy za-
for(int i=0; i<arg.length(); ++i) tem na Listing 3. W pierwszej linijce przedstawio-
histogram.add(arg.charAt(i)); nego Listingu konstruujemy nową listę obiektów
System.out.println(histogram); typu String. Warto w tym miejscu zauważyć jak
BiMap<String,String> en2PlDict = HashBiMap.create(); bardzo uproszczony jest ten proces dzięki zasto-
en2PlDict.put("bird", "ptak"); sowaniu metody Lists.newArrayList(...).
en2PlDict.put("sky", "niebo"); W dalszej kolejności wykorzystujemy algorytm
en2PlDict.put("star", "gwiazda"); Lists.transform(...) w celu przekształcenia
en2PlDict.put("bell", "dzwon"); zadanej listy zgodnie z przekazanym obiektem
en2PlDict.put("tree", "drzewo"); funkcyjnym. W naszym przypadku przekształ-
en2PlDict.put("gate", "brama"); camy listę napisów na listę odpowiadających im
en2PlDict.put("house", "dom"); wartości numerycznych. Miłym dodatkiem w
System.out.println(en2PlDict); bibliotece GCL jest klasa Joiner, która pozwa-
System.out.println(en2PlDict.inverse()); la w łatwy sposób przekształcać zadany kontener
} w pożądany napis. Po uruchomieniu programu
} przedstawionego na Listingu 3 otrzymamy na-
stępujący wynik:
Listing 2. Typowy przykład wykorzystania niemodyfikowalnego zbioru liczb całkowitych w stylu JCF
public static final Set<Integer> MY_HAPPY_NUMBERS; 3, 5, 7
static {
Set<Integer> set = new HashSet<Integer>(); W klasach Lists, Sets i Maps znaleźć można ca-
set.add(0); ły szereg operacji ułatwiających życie programi-
set.add(1); sty pracującego z kontenerami w Javie – zain-
set.add(3); teresowanych Czytelników odsyłam do doku-
set.add(7); mentacji API biblioteki (patrz: ramka W sieci).
set.add(13);
set.add(44); Klasy użytkowe:
MY_HAPPY_NUMBERS = Collections.unmodifiableSet(set); Iterators i Iterables
} Na nieco podobnej zasadzie jak w przypad-
ku klas użytkowych opisanych w poprzednim

www.sdjournal.org 13
Biblioteka miesiąca

podpunkcie, klasy Iterators i Iterables Klasa Ordering klasę Ordering. Szczególną uwagę proponuję
z biblioteki GCL udostępniają szereg metod Klasa Ordering jest jednym z ciekawszych roz- zwrócić na dwie ostatnie linijki tego przykła-
– tym razem odpowiedzialnych za uprosz- wiązań zaaplikowanych w GCL. Pozwala ona du, a w szczególności na fragment:
czenie procesu iteracji po kolekcjach elemen- w łatwy i efektywny sposób łączyć różne kom-
tów. Spójrzmy zatem na Listing 4, który za- paratory w celu wykonywaniu elastycznych po- ordering = ordering.reverse().compound(firs
wiera próbkę możliwości zastosowania klasy równań elementów przechowywanych w kon- tNameComparator);
Iterables. tenerach. Załóżmy, że mając prostą (a wręcz
Tym razem wykorzystujemy statycz- podręcznikową) klasę Person (patrz: Listing 5) W tym miejscu tworzymy uszeregowanie
ną metodę filter(), która filtruje zada- chcielibyśmy mieć możliwość uszeregowania odwrotne do istniejącego i dodatkowo, przy
ną kolekcję i zwraca drugą kolekcję, w któ- obiektów tej klasy w kontekście poszczególnych pomocy metody compound, dołączamy do
rej wszystkie obiekty spełniają zadany pre- jej składowych pól. Zadanie to można bardzo ła- niego drugi komparator, który będzie brany
dykat. Oprócz operacji filtrowania, klasa two zrealizować przy pomocy GCL. Spójrzmy pod uwagę w sytuacji, gdy pierwszy kompa-
Iterables pozwala wykonywać łączenie ko- na Listing 6. rator stwierdzi iż obiekty są identyczne.
lekcji (concat), wyszukiwanie elementów W pierwszej kolejności tworzymy listę Jako pożyteczne zadanie dla dociekliwych
w kolekcji (find), zliczanie elementów w ko- obiektów klasy Person. Następnie konstruuje- Czytelników pozostawiam rozpisanie sobie na
lekcji (frequency) i wiele, wiele innych. Po- my instancje dwóch komparatorów: pierwszy kartce jak będzie wyglądać wyjście przedstawio-
dobnie działa klasa Iterators – tyle, że za- z nich porównuje obiekty według nazwiska zaś nego programu, a następnie uruchomienie go
miast obiektów typu Iterable przyjmuje drugi – według imienia danej osoby. Ostatnie na komputerze i zweryfikowanie wyników.
obiekty typu Iterator. dwie linijki pokazują jak można wykorzystać
Podsumowanie
Listing 3. Przykład wykorzystania klasy Lists W ramach podsumowania niniejszego artyku-
łu przedstawię główne powody, które (w mojej
import com.google.common.base.Function; opinii) sprawiają, że Google Collection Library
import com.google.common.base.Joiner; to rozwiązanie godne ze wszech miar uwagi:
import com.google.common.collect.Lists;
import java.util.List; • Dojrzałość: w momencie pisania niniej-
public class GclListing3 { szego tekstu GLC dostępna jest w wer-
public static void main(String[] args) sji 1.0 RC3 i z tego co obiecują autorzy
{ – na tym etapie interfejs biblioteki jest
List<String> strList = Lists.newArrayList("3", "5", "7"); już bardzo stabilny. Interfejs ten będzie
List<Integer> intList = Lists.transform(strList, new Function<String, zamrożony w momencie gdy biblioteka
Integer>() { oznaczona będzie wersją 1.0.
public Integer apply(String from) { • Stabilność i niezawodność: niewiele jest
return Integer.parseInt(from); na świecie miejsc, w których bibliote-
} ka kontenerów możne być tak dobrze
}); przetestowana jak w Google; korzystając
System.out.println(Joiner.on(", ").join(intList)); z GCL mamy pewność, iż rozwiązanie
} to zostało intensywnie zweryfikowane
} w najbardziej ekstremalnych warunkach
z możliwych (przetwarzanie olbrzymich
Listing 4. Przykład wykorzystania klasy Iterables ilości danych w przeróżnych konfigura-
import com.google.common.base.Joiner; cjach), a przy jego projektowaniu, budo-
import com.google.common.base.Predicate; waniu i optymalizowaniu brali udział
import com.google.common.collect.*; najlepsi specjaliści z branży.
import java.util.List; • Spójność z JCF: w odróżnieniu od innych,
public class GclListing4 { podobnych rozwiązań (np. Jakarta Com-
public static void main(String[] args) { mons Collections) GLC zachowuje peł-
List<String> names = Lists.newArrayList( ną kompatybilność z Java Collection Fram-
"Ola", work – stanowi niejako naturalne rozsze-
"Agnieszka", rzenie tej biblioteki i kontynuację filozofii,
"Monika", którą zapoczątkowali inżynierowie Sun'a.
null); W szczególności – obsługa typów uogól-
Iterable<String> filtered = Iterables.filter( nionych (ang. generics) jest w GCL zaimple-
names, new Predicate<String>() { mentowana w identyczny sposób jak w JCF.
public boolean apply(String input) { • Lekkość, niezależność i elastyczność: pacz-
return input == null || input.startsWith("O"); ka z biblioteką waży około 500kB, przy
} czym GCL jest rozwiązaniem niezależ-
}); nym (nie wymaga instalacji 15 innych bi-
bliotek od Google...) i elastycznym (łatwo
System.out.println(Joiner.on(" and ").useForNull("Rafal").join(filtered)); dopasować je do swoich potrzeb).
} • Dokumentacja: jest pełna i profesjonal-
} nie przygotowana. Gorąco polecam jej
przeczytanie – chociażby w celu zapo-

14 12/2009
Google Collections Library

znania się ze wszystkim komponentami przez Google. W momencie pisania tego arty- dobrze współpracuje z Guava i równoległe ko-
dostępnymi w ramach GCL. kułu Guava składa się z trzech pakietów: rzystanie z tych dwóch rozwiązań daje bar-
• Licencja: Apache License 2.0, która daje dzo dobre efekty. Jednakże jest to temat na od-
możliwość wykorzystania GCL w komer- • com.google.common.primitives dzielny artykuł, który w niedługim czasie Czy-
cyjnych projektach bez żadnych ograniczeń. • com.google.common.io telnicy SDJ będą mogli przeczytać na łamach
• com.google.common.util.concurrent kolumny Biblioteka Miesiąca. Na ten moment
Co więcej – według zapowiedzi autorów, Go- zachęcam do eksperymentów z Google Collec-
ogle Collection Library stanie się w niedługim Google Collection Library ma zostać dołączo- tion Library i dziękuję za czas poświęcony na
czasie częścią bibliteki Guava – łączącej w so- na do tego grona w momencie uzyskania statu- przeczytanie powyższego tekstu.
bie cały szereg użytecznych bibliotek i kom- su pełnej wersji 1.0 i zamrożenia API. Na ten
ponentów ogólnego użytku wypracowanych moment wiadomo już jednak, że GCF bardzo RAFAŁ KOCISZ
Pracuje na stanowisku Dyrektora Technicznego
w firmie Gamelion, wchodzącej w skład Grupy
W Sieci BLStream. Rafał specjalizuje się w technologiach
• http://code.google.com/p/google-collections/ – strona domowa Google Collection Library związanych z produkcją oprogramowania na plat-
• http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?http://google-collectio formy mobilne, ze szczególnym naciskiem na two-
ns.googlecode.com/svn/trunk/javadoc/com/google/common/collect/package-summary.html – rzenie gier. Grupa BLStream powstała, by efektyw-
dokumentacja API biblioteki GCL niej wykorzystywać potencjał dwóch szybko roz-
• http://www.javalobby.org/articles/google-collections/ – wywiad z autorami GCL przedstawiają-
wijających się producentów oprogramowania –
cy genezę biblioteki
• http://publicobject.com/2007/09/series-recap-coding-in-small-with.html – szereg praktycznych BLStream i Gamelion. Firmy wchodzące w skład
przykładów wykorzystania GCL grupy specjalizują się w wytwarzaniu oprogramo-
• http://www.youtube.com/watch?v=ZeO_J2OcHYM – video-prezentacja biblioteki (część pierwsza) wania dla klientów korporacyjnych, w rozwiąza-
• http://www.youtube.com/watch?v=9ni_KEkHfto – video-prezentacja biblioteki (część druga) niach mobilnych oraz produkcji i testowaniu gier.
Kontakt z autorem: rafal.kocisz@game-lion.com

Listing 5. Implementacja przykładowej klasy Person


public class Person { }
private String firstName; public String getLastName() {
private String lastName; return lastName;
public Person(String firstName, String lastName) { }
this.setFirstName(firstName); public void setLastName(String lastName) {
this.setLastName(lastName); this.lastName = lastName;
} }
public String getFirstName() { @Override
return firstName; public String toString() {
} return getFirstName() + " " + getLastName();
public void setFirstName(String firstName) { }
this.firstName = firstName; }

Listing 6. Przykład wykorzystania klasy Ordering

import com.google.common.base.Joiner; }
import com.google.common.base.Predicate; };
import com.google.common.collect.*; Comparator<Person> firstNameComparator = new
import java.util.Comparator; Comparator<Person>() {
import java.util.List; public int compare(Person p1, Person p2) {
public class GclListing6 { return p1.getFirstName().compareTo(p2.getFi
public static void main(String[] args) { rstName());
List<Person> persons = Lists.newArrayList( }
new Person("Jan", "Kowalski"), };
new Person("Adam", "Malinowski"), Ordering<Person> ordering = Ordering.from(lastNameC
null, omparator);
new Person("Tadeusz", "Nowak"), System.out.println(ordering.nullsLast().sortedCopy(
null, persons));
new Person("Zenon", "Kowalski")); ordering = ordering.reverse().compound(firstNameCom
Comparator<Person> lastNameComparator = new parator);
Comparator<Person>() { System.out.println(ordering.nullsLast().sortedCopy(
public int compare(Person p1, Person p2) { persons));
return p1.getLastName().compareTo(p2.getLas }
tName()); }

www.sdjournal.org 15
Klub techniczny Adobe

Połączenie Flex
oraz Flash CS3/CS4
Szybkie tworzenie własnych komponentów

Poznajemy rozszerzenie dla programu Flash CS3/CS4 pozwalające na


szybkie tworzenie komponentów oraz kontenerów dla frameworka
Flex. Wykorzystamy podejście niewymagające znajomości architektury
komponentów, a pozwalające znacznie wzbogacić nasze aplikacje.
tym celu otwieramy nowy dokument, ko-
Dowiesz się: Powinieneś wiedzieć: niecznie musi to być dokument dla Action-
• Jak projektować i wykorzystywać własne • Podstawy technologii Flex; Scipt 3.0. Z menu aplikacji wybieramy po-
komponenty i kontenery w środowisku • Posługiwać się programem Flash. lecenie Insert>New Symbol i nadajemy mu
Flash CS3/CS4. nazwę FlashLabel. Następnie projektujemy
wygląd naszego komponentu zgodnie z wła-
snym pomysłem i umiejętnościami. Pamię-
wia ona łatwe zarządzanie roszerzeniami dla tajmy jedynie, aby obowiązkowo dodać do
programów takich jak Adobe Flash, Adobe niego przynajmniej jedno dynamiczne po-
Poziom Photoshop itp. W większości przypadków le tekstowe o nazwie textField. Przykłado-
trudności wystarczy dwukrotnie kliknąć na rozszerze- wy projekt został przedstawiony na Rysun-
nie, aby je zainstalować. Jeśli zostało ono za- ku 2. Wybierając z menu aplikacji polece-
instalowane poprawnie, w menu aplikacji nie Convert symbol to Flex component, z nasze-
Flash w zakładce Commands powinny poja- go obiektu utworzony zostanie komponent o

T
worząc aplikację w technologii Flex, wić się dwie nowe pozycje (Rysunek 1): nazwie FlashLabel. Wygenerowana w ten spo-
mamy do dyspozycji znaczny ze- sób klasa FlashLabel dziedziczy po klasie
staw komponentów, które może- • Convert symbol to Flex component (za- mx.flash.UIMovieClip. Jest to klasa bazowa
my wykorzystać. Czasami jednak pojawia mień symbol na komponent) dla wszystkich komponentów tworzonych w
się potrzeba stworzenia własnego kompo- Polecenie to konwertuje wybrany przez aplikacji Flash. Klasa ta implementuje inter-
nentu, co pociąga za sobą konieczność za- nas obiekt na komponent, który może fejsy, które pozwalają traktować obiekt klasy
poznania się z ich architekturą. Jest jednak być następnie wykorzystany w aplika- FlashLabel jak standardowy komponent.
prostszy sposób – rozszerzenie Flex Compo- cjach Flex oraz AIR; Na tym etapie komponent FlashLabel mo-
nent Kit for Flash dostarczane przez firmę • Convert symbol to Flex container ( zamień że już być poprawnie wyświetlony w aplika-
Adobe. Pozwala ono szybko i wygodnie two- symbol na kontener). cji Flex wraz z innymi standardowymi kom-
rzyć unikatowe komponenty. Jest też świet- Polecenie to zamienia zaprojektowa- ponentami. Aby możliwa była zmiana wy-
nym sposobem wprowadzenia do technolo- ny we Flashu obiekt na kontener. Dzię- świetlanego tekstu, musimy jeszcze dopi-
gii Flex osób dotychczas związanych z apli- ki temu możemy w powiększyć kolek- sać kilka linijek kodu. W tym celu tworzy-
kacją Flash. Rozwiązanie to świetnie wpaso- cję konetenerów dostępnych w pakie- my we Flashu nowy dokument ActionScript
wuje się w koncepcję Flash Platform, dając cie mx.containers. Kontenery są specja- i nadajemy mu nazwę FlashLabel. Ważne jest,
możliwość łączenia umiejętności programi- nymi komponentami, na których może- aby znalazł się on w tym samym katalogu co
stycznych ze zdolnościami graficznymi i po- my umieszczać inne komponenty. Przy- utworzony poprzednio dokument z kompo-
mysłowością. kładem może być kontener mx.conta- nentem i miał tę samą nazwę co komponent.
iners.TitleWindow. Na Listingu 1 przedstawiony został kod doda-
Konfiguracja Flash CS3/CS4 jący do naszego komponentu właściwość la-
W pierwszym kroku musimy pobrać ze Po wykonaniu tych czynności jesteśmy goto- bel. Pozwala ona na zapisanie i odczytanie ak-
strony Adobe (http://www.adobe.com/go/ wi do stworzenia pierwszego komponentu z tualnie wyświetlanego tekstu. W analogicz-
flex3_cs3_swfkit) wspomniany dodatek. Po- wykorzystaniem Flash CS3 lub CS4. ny sposób możemy do naszego komponentu
brana paczka jest w formacie mxp. Format wprowadzać dowolną funkcjonalność.
ten jest obsługiwany przez aplikację Adobe Przykładowy komponent Kiedy nasz komponent jest już goto-
Extension Manager, która jest rozpowszech- W pierwszym przykładzie stworzymy we Fla- wy, możemy go opublikować. W wyniku
niana razem z produktami Adobe. Umożli- shu pole tekstowe z dodaną animacją tła. W otrzymamy, oprócz standardowo tworzo-

16 12/2009
Połączenie Flex oraz Flash CS3/CS4

nego pliku *.swf, również plik w formacie


*.swc. Jest to format biblioteki przechowu-
jącej skompilowany kod. Aby wykorzystać
utworzoną bibliotekę z naszym komponen-
tem, przechodzimy do Flex Buildera, two-
rzymy nowy projekt Flex i otwieramy okno
ustawień projektu. Następnie przechodzi-
my do zakładki Flex Build Path. Rysunek 3
przedstawia zrzut okienka ustawień z wy-
braną zakładką Library path, gdzie dodaje-
my naszą bibliotekę (przycisk Add SWC...). Rysunek 1. Nowe polecenia w menu aplikacji Flash
Na tym etapie nasz komponent jest już go-
towy do użycia. Listing 2 przedstawia spo-
sób jego wykorzystania. W przykładzie tym
dodaliśmy dodatkowo standardowy kom-
ponent TextArea, który połączyliśmy z na-
szym komponentem w taki sposób, że kie-
dy użytkownik wpisze w nim jakiś tekst,
zostanie on także wyświetlony w naszym
komponencie. Działająca aplikacja korzy-
stająca z komponentu zaprojektowanego
na Rysunku 2 została przedstawiona na Ry-
sunku 4.

Przykładowy kontener
W kolejnym przykładzie zaprojektujemy
jeszcze jeden komponent, teraz będzie to
jednak kontener. W aplikacjach opartych
o framework Flex kontenerami są wszyst-

Listing 1. Kod ActionScipt dla naszego Rysunek 2. Zaprojektowany komponent tekstowy


komponentu

package {
import mx.flash.UIMovieClip;

public class FlashLabel extends


UIMovieClip
{
public function FlashLabel():void
{
super();
};

// metoda ustawiająca wyświetlany


tekst
public function set label( val:
String ):void
{
textField.text = val;
};

// metoda pobierająca aktualnie


wyświetlany
tekst
public function get label():
String
{
return textField.text;
};
};
};
Rysunek 3. Okno dodawania biblioteki swc do projektu

www.sdjournal.org 17
Klub techniczny Adobe

kie obiekty z pakietu mx.containers, na


Listing 2. Użycie naszego komponentu w aplikacji Flex
których możemy umieszczać inne kom-
<?xml version="1.0" encoding="utf-8"?> ponenty. Aby stworzyć własny kontener,
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" projektowanie rozpoczynamy podobnie
layout="vertical" jak w przypadku komponentu FlashLabel.
verticalAlign="middle" Tworzymy nowy symbol, nadając mu na-
xmlns:local="*"> zwę FlashContainer, i uruchamiamy wła-
sne zdolności graficzne. Kiedy nasz kom-
<!-- nasz komponent zaprojektowany we Flash'u –> ponent jest już gotowy, wybieramy z me-
<local:FlashLabel label="{inTekst.text}" /> nu aplikacji polecenie Convert to Flex con-
tainer. Po chwili mamy już prawie gotowy
<mx:HBox> kontener. Pozostaje nam jeszcze tylko okre-
<mx:Label text="Wpisz tekst : " /> ślić miejsce, gdzie będą dodawane obiekty
<!-- wprowadzony tu tekst będzie przekazywany do naszego komponentu –> na nim umieszczane.
<mx:TextArea textAlign="center" id="inTekst" text="Pisz !"/> W wyniku przeprowadzonej konwersji
</mx:HBox> w bibliotece projektu pojawił się również dodat-
kowy element o nazwie FlexContentHolder.
</mx:Application> Obiekt ten należy umieścić w naszym konte-
nerze. Modyfikując jego położenie oraz rozmiar,
określamy obszar dostępny dla obiektów potom-
nych. Na tym etapie mamy już w pełni funkcjo-
nalny kontener.
Zanim jednak przejdziemy do wykorzy-
stania naszego konteneru, wykorzystamy
jeszcze jedną dostępną funkcjonalność, ja-
ką jest obsługa stanów wizualnych. Dzięki
temu możemy stworzyć kontener pozwala-
jący użytkownikowi ukryć jego zawartość
(zwinąć widok). Przejścia pomiędzy zde-
finiowanymi stanami mogą być animowa-
ne, my jednak ograniczymy się do najprost-
szego przełączenia. Rysunk 5 przedsta-
wia przykładowy kontener z obsługą sta-
nu. Jak widać na rysunku, komponent po-
siada specjalną warstwę o nazwie states;
nazwy umieszczonych tam klatek określa-
ją dostępne stany.
Pozostałe widoczne warstwy odpowia-
dają za wygląd naszego kontenera. Wi-
doczny jest obszar, w którym umieszcza-
ne będą obiekty potomne – ciemny pro-
stokąt z napisem < Fx >. Analogicznie jak
w poprzednim przypadku dodaliśmy tak-
Rysunek 4. Komponent zaprojektowany na Rysunku 2 w działaniu
że pole tekstowe wyświetlające tytuł kon-
tenera. Całość kodu, jaką musimy dopi-
sać do kontenera, została przedstawiona
na Listingu 3. Obiekt this.content jest
tworzony w czasie kompilacji i powiąza-
ny z umieszczonym na scenie obiektem
FlexContentHolder.
Ograniczeniem tworzonych w ten spo-
sób kontenerów jest możliwość umiesz-
czenia na nich tylko jednego obiektu. Jed-
nak obiektem tym może być jeden ze stan-
dardowych kontenerów. W ich przypad-
ku natomiast nie mamy już żadnych ogra-
niczeń. Działający kontener oraz jego dwa
stany – rozwinięty i zwinięty, są widoczne
na Rysunku 6.

Podsumowanie
Jak widać po przykładach, w prezentowa-
Rysunek 5. Przykładowy projekt kontenera nym rozszerzeniu drzemią wielkie moż-

18 12/2009
Połączenie Flex oraz Flash CS3/CS4

liwości. Dzięki niemu komponenty mo-


gą tworzyć użytkownicy nie posiadają-
cy dogłębnej wiedzy na temat architek-
tury komponentów. Tworzenie nowych
komponentów, jak i ich wykorzystanie
jest bardzo proste i szybkie. Jedynym ist-
niejącym ograniczeniem jest wyobraźnia
projektanta. Kody źródłowe omawianych
aplikacji znajdują się na stronie – http://
segfaultlabs.com/docs

MATEUSZ MAŁCZAK
Autor jest doświadczonym programistą tworzą-
cym aplikacje desktopowe dla systemów Win-
dows i Linux. Aktualnie bardzo mocno związany
z technologiami Flex/AIR. Tworząc aplikacje dla
serwisu http://komixo.com wygrał ogólnopolski
konkurs na najlepszą aplikację w tej technologii
(http://www.flexchallenge.com/pl).
Rysunek 6. Użycie naszego kontenera w aplikacji Kontakt z autorem: matuesz@malczak.info

W Sieci
• http://www.adobe.com/go/flex3_cs3_swfkit – dodatek do aplkacji Flash CS3/CS4 umożliwiający tworzenie komponentów.

Listing 3. Kod dla naszego kontenera z obsługą stanów

package { titleField.visible = false;


import flash.display.MovieClip; currentState = "close";
import flash.events.MouseEvent; } else {
import mx.flash.ContainerMovieClip; // pokaż zawartość kontenera i przejdź do stanu
'otwarty'
public class FlashContainer extends ContainerMovieClip { this.content.visible = true;
titleField.visible = true;
public function FlashContainer():void currentState = "open";
{ };
// ustawiamy obsługę zdarzenia }
hideShowButton.addEventListener(MouseEvent.CLICK,
arrowHandler); // metoda ustawiająca tytuł dla naszego kontenera
hideShowButton.useHandCursor = true; public function set title( val:String ):void
hideShowButton.buttonMode = true; {
} titleField.text = val;
};
// przełączenie do odpowiedniego stanu kiedy
użytkownik kliknie obiekt po prawiej // metoda pobierająca tytuł dla naszego kontenera
// przejście to możemy zaprojektować jako animację public function get title():String
private function arrowHandler(event:MouseEvent):void {
{ return titleField.text;
if ( currentState == "open") { };
// przechodząc do stanu 'zamknięty' ukrywany }
zawartość kontenera }
this.content.visible = false;

www.sdjournal.org 19
Klub techniczny Progress Software

Technologie
Progress OpenEdge
Część 4. Serwer Aplikacji OpenEdge

Serwer Aplikacji (AppServer) OpenEdge jest potężnym silnikiem


transakcyjnym opartym na nowoczesnych standardach. Zapewnia
bezpieczne zarządzanie b. dużymi transakcjami, niezależność od
interfejsu użytkownika oraz możliwość stworzenia aplikacji zgodnej ze
standardami architektury zorientowanej na serwisy (SOA).
• zapewnienie nieprzerwanej pracy w
Dowiesz się: Powinieneś wiedzieć: przypadku awarii serwera głównego
• O uruchamianiu procedur na Serwerze Apli- • Na czym polega koncepcja przetwarzania dzięki funkcjom load balancing i fault to-
kacji OpenEdge; rozproszonego. lerance;
• O trybach pracy AppServera; • zmniejszenie wymagań sprzętowych
• Co to są partycje i do czego służą. i programistycznych dzięki wprowadze-
niu zarządzania stanem procesów;
• zmniejszenie obciążenia sieci (mniej da-
nych przesyłanych do klienta);
który udostępnia klientom Java, .NET(C#), • zwiększenie poziomu zabezpieczeń da-
C, C++ i web serwisom transparentny dostęp nych (klient nie ma bezpośredniego do-
Poziom do logiki biznesowej na AppServerze. stępu do źródeł danych).
trudności Na Rysunku 1 przedstawiono schemat
przetwarzania rozproszonego w architektu- Architektura
rze n-warstwowej. Różne typy klientów mają Serwera Aplikacji OpenEdge
swobodny dostęp do logiki biznesowej, znaj- Rysunek 2 przedstawia architekturę, w jakiej

N
ajistotniejszym i koniecznym ele- dującej się na dedykowanych serwerach apli- pracuje Serwer Aplikacji OpenEdge, i sche-
mentem w procesie tworzenia kacji. Każdy AppServer ma z kolei dostęp do mat obsługi żądania.
aplikacji rozproszonej jest po- źródeł danych. Do takiej architektury łatwo
dział na interfejs użytkownika i logikę biz- dodać dalsze serwery (aplikacji bądź baz da- • Admin Server – proces pozwalający za-
nesową. Ta ostatnia uruchomiona na ser- nych), poprawiając skalowalność, balansowa- rządzać innymi komponentami archi-
werach aplikacji stanowi centralny punkt nie obciążeniem (load balancing) i zabezpie- tektury OpenEdge;
architektury aplikacji. Czy pamiętają Pań- czając się przed awarią sprzętową (fault tole- • Name Server – zarządza listą dostęp-
stwo założenia Architektury Referencyjnej rance). nych AppServerów i listą nazw obsłu-
OpenEdge (Technologie Progress OpenEdge Istnieje zatem wiele zalet związanych giwanych przez nie Serwisów Aplika-
część 1. SDJ 09/2009), w których była mo- z zastosowaniem modelu architektury n-war- cji (Application Service Name); prze-
wa o podziale aplikacji w dwóch wymia- stwowej i Serwera Aplikacji OpenEdge. Naj- kierowuje żądania klienta do App-
rach? Wyszczególnione w tej architektu- ważniejsze z nich to: Servera w oparciu o nazwę Serwisu
rze centralne warstwy dostępu do danych Aplikacji.
i serwisów biznesowych powinny znajdo- • bezpośredni dostęp do centralnej logiki
wać się na AppServerze. biznesowej dla różnych typów klientów Sam serwer aplikacji składa się z dwóch kom-
Aplikacje biznesowe, które wykorzystu- i systemów aplikacyjnych; ponentów:
ją Serwer Aplikacji OpenEdge, mogą obsłu- • oddzielenie fizycznej lokalizacji sprzęto-
giwać dowolny interfejs użytkownika. Przy wej od samej aplikacji, zapewniające ela- • AppServer Broker, którego zadaniem
pomocy narzędzi deweloperskich, takich jak styczność w konfiguracji i łatwość utrzy- jest: zarządzanie połączeniami pomię-
OpenEdge Architect, AppBuilder, można mania środowiska aplikacji; dzy klientami i pulą Agentów AppSe-
stworzyć aplikację w języku ABL, XML czy • obsługa przetwarzania asynchroniczne- rvera oraz przekierowywanie do nich
HTML. Wykorzystując technologię Open go, co zwiększa liczbę obsłużonych żą- żądań klientów; zarządzanie pulą Agen-
Client, można wygenerować tzw. kod proxy, dań w tym samym czasie; tów AppServera.

20 12/2009
Część 4. Serwer Aplikacji OpenEdge

• AppServer Agent – procesy przyłączo-


������
ne do źródeł danych, które obsługują żą-
dania klienta, uruchamiają odpowiednie
���
procedury ABL.
����������������

Schemat obsługi żądania jest następu-


jący: proces klienta kontaktuje się z Na- ���� ����������������
����������������������������
me Serverem, który zwraca informację
potrzebną do połączenia się z instancja- ������
�����������
mi serwera aplikacji obsługującymi odpo- ����
wiednią nazwę Serwisu Aplikacji; klient
��������������
łączy się z brokerem AppServera w odpo-
wiednim trybie; broker zestawia połącze- ����������������
���
nie. Żądania klienta są już teraz obsługi-
wane bezpośrednio przez procesy agen-
����������������
tów lub przez broker (w zależności od try- ������������
�������
bu pracy). ����
Zarządzanie Serwerem Aplikacji i usta-
wianie jego parametrów może odbywać
���
się z poziomu narzędzia Progress Explo-
������
rer Tool (przykład ustawień na Rysunku
3) bądź OpenEdge Explorer (od OpenEd-
ge 10.2A).
Rysunek 1. Model architektury n-warstwowej
Wykonywanie
procedur na AppServerze
Przykładowe przyłączenie się do serwera apli- ����
��
kacji i wywołanie na nim procedury pokaza- ������
ne jest na Listingu 1.
Przyłączenie odbywa się poprzez Name �����
�� ������
Server (nr portu 5162), który wyszuku-
je proces serwera aplikacji z nazwą Serwisu ��
Aplikacji asMyService. Opcjonalnie możemy
podać id użytkownika i hasło. W tym przy- �� ���������
����������
padku gdyby połączenie nie mogło być zre- �����������
alizowane, wskaźnikiem do serwera staje się �����������������
wskaźnik do bieżącej sesji lokalnej i w niej ��
nastąpi wywołanie procedury.
�����������������
Jak widać, w parametrach przyłączenia
nie ma nazwy fizycznej AppServera. Co ����
������
���� ������
się stanie, jeśli kilka serwerów aplikacji bę-
���������
dzie miało w ustawieniach tę samą nazwę
Serwisu Aplikacji (ustawienie tego para-
metru pokazano na Rysunku 3)? W tym
przypadku żądania kierowane do serwisu Rysunek 2. Schemat pracy AppServera w architekturze OpenEdge
o tej samej nazwie rozłożą się na kilka Ap-
pServerów.
Administrator ma dodatkowo możliwość
zdefiniowania udziału w procentach po-
szczególnych serwerów w obsłudze tych żą-
dań, tzw. priority weight, co pokazano na Ry-
sunku 4.
Bardzo istotnym jest fakt, że jeśli AppSe-
rvery będą skonfigurowane fizycznie na róż-
nych maszynach, to awaria jednej z nich nie
spowoduje przerwy w obsłudze żądań.

Partycje
Partycje w technologii Serwera Aplika-
cji OpenEdge to logiczny zbiór procedur
ABL przypisanych do określonego fizycz-
nie miejsca w sieci. Partycja może być zde-
finiowana jako lokalna (localhost) lub zdal- Rysunek 3. Progress Explorer Tool – ustawienie nazwy Serwisu Aplikacji

www.sdjournal.org 21
Klub techniczny Progress Software

na (dla określonego Serwisu Aplikacji na


Listing 1. Przyłączenie się do AppServera zdalnej maszynie). Zaletą partycji jest to,
DEFINE VARIABLE lReturn AS LOGICAL NO-UNDO. że jej parametry są ustawiane poza kodem
DEFINE VARIABLE hServer AS HANDLE NO-UNDO. programu. Dzięki temu można zmieniać
CREATE SERVER hServer. konfigurację bez konieczności rekompila-
lReturn = hServer:CONNECT("-S 5162 -H localhost -AppService asMyService","use cji logiki biznesowej (np. przy przeniesie-
rid","password",?). niu aplikacji ze środowiska testowego do
IF NOT lReturn THEN DO: produkcyjnego).
MESSAGE "Nie można połączyć się z AppServerem." VIEW-AS ALERT-BOX Parametry ustawia się w narzędziu Service
hServer = SESSION:HANDLE. Parameter Maintenance (Rysunek 5).
END.
RUN procedura1.p ON hServer (INPUT paramIn, OUTPUT paramOut). Tryby pracy AppServera
/* Odłączenie się od AppServara */ Do tej pory mówiliśmy o przyłączaniu się do
hServer:DISCONNECT(). AppServera i wykonywaniu na nim procedur,
ale nie wspomnieliśmy o trybach jego pracy.
Listing 2. Przyłączenie i odłączenie się od AppServera poprzez partycje Są trzy tryby dla modelu Session-managed:
... Stateless, State-aware, State-reset oraz jeden dla
RUN appServerConnect(„myPartition", ?, ?, OUTPUT h_myPartition). modelu Session-free: State-free. Poniżej przed-
stawiam ich krótką charakterystykę.
/* Drugi parametr jest typu logicznego i określa, czy należy podać id i
hasło, trzeci parametr to tzw. app_server_info – tekst • Stateless – Połączenie w tym trybie jest
wykorzystywany w przyłączeniach do różnych celów */ w rzeczywistości przyłączeniem do
procesu brokera, który dla obsłużenia
RUN procedura1.p ON h_myPartition (INPUT paramIn, OUTPUT paramOut). żądania przydziela jeden proces Agen-
... ta AppServera. Agent może więc obsłu-
/* Odłączenie się */ giwać żądania od wielu klientów. Jed-
RUN appServerDisconnect(INPUT „myPartition"). nocześnie żądania od jednego klienta
mogą być obsłużone przez kilku agen-
Listing 3. Związanie i zwolnienie Agenta AppServera w trybie Stateless tów. Klient może związać agenta na pe-
... wien czas poprzez uruchomienie zdal-
lReturn = hServer:CONNECT("-S 5162 -H localhost -AppService nej procedury w trybie Persistent lub
asMyService","userid","password",?). ustawiając atrybut na AppServerze
... SESSION:SERVER-CONNECTION-BOUND-
/* Związanie agenta */ REQUEST (Listing 3). Jest to rodzaj pracy,
RUN procedura2.p ON hServer PERSISTENT SET hProc. który wymaga większego nakładu pracy
/* Lub na AppServerze: SESSION:SERVER-CONNECTION-BOUND-REQUEST = TRUE */ programistów dla zachowania kontek-
... stu między żądaniami, lecz jest zaleca-
/* Zwolnienie agenta */ ny przy obsłudze dużej liczby żądań od
DELETE OBJECT hProc. wielu klientów, gdyż oferuje możliwość
/* Lub na AppServerze: SESSION:SERVER-CONNECTION-BOUND-REQUEST = FALSE */ osiągnięcia dużej wydajności oraz skalo-
walności.

Rysunek 4. Ustawienie udziału w procentach w obsłudze żądań dla serwera aplikacji Rysunek 5. Ustawianie parametrów partycji

22 12/2009
Część 4. Serwer Aplikacji OpenEdge

• State-free – Tryb ten przypomina State- przypisany dany Serwis Aplikacji. Tryb • State-aware – Klient jest połączony z de-
less, jako że agent nie jest przypisany do pracy State-free jest bardzo wygodny, dykowanym Agentem AppServera do
jednego klienta. W odróżnieniu od Sta- gdy żądania nie zależą od siebie i mogą momentu fizycznego odłączenia. Żąda-
teless klienci nie przyłączają się do bro- być wykonane w dowolnej kolejności. nia są kierowane bezpośrednio do agenta
kera, ale logicznie do Serwisu Aplikacji. Stosuje się go do równoległego wywoły- z pominięciem brokera. Kontekst (przy-
Żądanie klienta może być więc obsłużo- wania żądań asynchronicznych (Listing łączone bądź odłączone bazy danych,
ne przez dowolny AppServer, który ma 4) oraz wystawiania WebSerwisów. zmienne globalne, procedury Persistent
wystartowane lokalnie na serwerze apli-
Listing 4. Obsługa żądań asynchronicznych kacji) jest zachowany między połączenia-
mi. Tryb ten jest wygodny ze względu na
... większą łatwość zarządzania kontekstem,
/* Połączenie state-free */ ale nie jest zalecany dla aplikacji przezna-
lReturn = hServer:CONNECT("-S 5162 -H localhost -AppService asMyService czonych do pracy w środowisku o dużej
-sessionModel Session-free"). skalowalności.
• State-reset – Jest to tryb bardzo podobny do
RUN runAsynch2.p ON hServer ASYNCHRONOUS SET hAsync2 EVENT-PROCEDURE "pause2". State-aware. Różnica polega na tym, że przy
RUN runAsynch1.p ON hServer ASYNCHRONOUS SET hAsync1 EVENT-PROCEDURE "pause1". połączeniu kontekst jest resetowany do sta-
nu ustalonego w procedurze startowej.
PROCEDURE pause1:
MESSAGE "Pause 3 sec. finished" Na zakończenie powiem kilka słów na te-
VIEW-AS ALERT-BOX INFO BUTTONS OK. mat definiowania granic transakcji. W ko-
END. dzie języka ABL transakcje zdefiniowane są
z tzw. blokiem transakcyjnym (np. blok, DO
PROCEDURE pause2: TRANSACTION...END czy blok całej procedu-
MESSAGE "Pause 10 sec. finished" ry). W technologii przetwarzania transak-
VIEW-AS ALERT-BOX INFO BUTTONS OK. cyjnego pojawia się problem określenia gra-
END. nic transakcji, gdyż mamy do czynienia z kil-
koma sesjami (klient, serwer aplikacji). De-
/* Na AppServerze wystartowano 2 procedury asynchronicznie. Czas wykonania weloper na ogół powinien dążyć to tego, aby
pierwszej wynosi ok. 10 sek., a drugiej 3 sek. W trybie transakcja zawarła się w określonym żąda-
State-free wyniki z wywołań procedur nie są kolejkowane, niu, jednakże czasem zachodzi potrzeba roz-
więc najpierw pojawi się komunikat o zakończeniu procedury, szerzenia jej na wiele żądań. W logice bizne-
która była wywołana jako druga, a później tej wywołanej jako sowej można to zrealizować, definiując tzw.
pierwsza. */ transakcje automatyczne (Listing 5).
Kończąc temat Serwera Aplikacji, zapra-
Listing 5. Transakcje automatyczne szam na następny odcinek poświęcony tech-
/* Zainicjowanie transakcji automatycznych – uruchomienie procedury typu nologiom XML zaimplementowanym w śro-
Persistent */ dowisku OpenEdge.
RUN startTransaction.p ON hServer PERSISTENT SET hStart.

Progress i OpenEdge są znakami towarowy-


/* gdzie procedura startTransaction.p ma pierwszą instrukcję: */ mi lub zarejestrowanymi znakami towaro-
TRANSACTION-MODE AUTOMATIC. wymi firmy Progress Software Corporation
lub jednej z jej oddziałów lub spółek zależ-
nych w USA i innych krajach. Wszelkie inne
/* Zatwierdzenie transakcji*/
znaki towarowe zawarte w powyższym są
RUN finishTransaction.p ON hServer.
własnością ich właścicieli. Dane techniczne
mogą ulec zmianie bez powiadomienia.
/* gdzie procedura finishTransaction.p ma postać */
DEFINE VARIABLE hTran AS HANDLE.
hTran = THIS-PROCEDURE:TRANSACTION. PIOTR TUCHOLSKI
hTran:SET-COMMIT(). Autor jest od 12 lat związany z technologia-
/* Lub wycofanie transakcji*/ mi Progress Software. Wieloletni konsultant
hTran:SET-ROLLBACK(). i Kierownik Działu Szkoleń Progress Software
/* Fizyczne kończenie transakcji*/ sp. z o.o., specjalizujący się w technologii Ope-
DELETE OBJECT hStart. nEdge.
Kontakt z autorem: piotr.tt@gmail.com

W Sieci
• http://communities.progress.com/pcom/community/psdn/openedge – Progress Software Developers Network, część ukierunkowana na za-
gadnienia techniczne związane z OpenEdge®;
• http://web.progress.com – strona Progress Software Corporation;
• http://www.progress.com/pl – strona Progress Software sp z o.o.

www.sdjournal.org 23
Programowanie C++

Metaprogramowanie
algorytmy wykonywane w czasie kompilacji

Metaprogramowaniem nazywa się tworzenie programów, które


w wyniku działania dostarczają programów. Metaprogramy stosujemy
aby zwiększyć szybkość działania programów oraz ich czytelność,
a także aby unikać powielania kodu, wtedy gdy te same operacje
chcemy wykonać dla grupy typów.
tu rzeczywistego. Szablonu tego używa się za-
Dowiesz się: Powinieneś wiedzieć: miast funkcji kwadratowej, Power<2>(x) do-
• Jak tworzyć programy działające w czasie • Jak pisać proste programy w C++; starcza funkcji x*x, Power<3>(x) dostarcza
kompilacji; • Co to są szablony. x*x*x itd. Szablon ten, przedstawiony na Li-
• Jak używa się kolekcji typów; stingu 3, po pierwsze skraca zapis, po drugie
• Jak wykorzystać kontenery i algorytmy do- kod jest bardziej czytelny, po trzecie kod wy-
starczane przez bibliotekę boost::mpl. konuje się szybciej niż implementacja algoryt-
mu potęgowania wykonywana w czasie działa-
nia programu, dostarczana przez std::power,
nywalnego, dostarczeniu stałej równej N! , po- która zawiera pętlę wykonującą wielokrotne
nieważ wartość składowej value dla tego szablo- mnożenie.
Poziom nu jest obliczana w czasie kompilacji. Szablon Jeżeli wykładnik jest dużą liczbą całkowi-
trudności Silnia nadaje znaczenie stałej, więc kod jest tą (co zdarza się na przykład przy kodowaniu
bardziej czytelny, nie musimy obliczać warto- RSA), to algorytm potęgowania przez wielo-
ści funkcji narzędziem zewnętrznym, unikamy krotne mnożenie, przedstawiony na Listingu 3,
pomyłek związanych z umieszczaniem w pro- jest mało wydajny. Wielkość kodu wynikowego

N
arzędziami do metaprogramowa- gramie wartości obliczonych poza nim. może znacznie wzrosnąć, ponieważ utworzo-
nia w C++ jest preprocesor oraz me- Algorytm obliczający silnię (Listing 1) zo- na w czasie kompilacji funkcja jest długa. Nedo-
chanizm szablonów, mechanizmy te stał zdefiniowany rekurencyjnie i wykorzy- godności powyższe możemy usunąć, korzysta-
dostarczają instrukcji wyższego rzędu, które po- stuje specjalizację szablonów. Rekurencja jest jąc z faktu, że w szablonach można implemen-
zwalają na manipulacje kodem źródłowym. techniką bardzo powszechną w tego typu pro- tować złożenia funkcji. Ponieważ xn, dla n=2m
Mechanizm szablonów oferuje większe moż- gramach, ponieważ metaprogramy mogą wy- (gdy n jest potęgą dwójki, tzn. n=1,2,4,8,16,
liwości i wygodniejszą składnię niż makrode- korzystywać jedynie byty dostępne w czasie itd.) można obliczać jako m krotne podnosze-
finicje preprocesora, szablony są lepiej wspie- kompilacji (stałe całkowite, typy itd.), nie mo- nie do kwadratu, czyli xn=(...((x2)2)...)2, jeże-
rane przez kompilator, dlatego są częściej uży- żemy użyć zmiennej ani tworzyć pętli. Meta- li podnoszenie do kwadratu oznaczymy jako
wane do tworzenia metaprogramów i my także programy są podobne do programów tworzo- sqr, to xn=sqr × sqr × … × sqr(x), gdzie × ozna-
będziemy z nich korzystać, pomijając możliwo- nych w językach funkcyjnych. Program po- cza złożenie funkcji. Używając tego sposobu
ści tworzenia takich rozwiązań przez preproce- kazany na Listingu 2 (zaczerpnięty z książki dla n=1024, wykonamy tylko m=10 operacji
sor. Szablony umożliwiają implementację do- Abrahams, Gurtovoy, Język C++, metaprogra- podnoszenia do kwadratu, a nie 1024 mnoże-
wolnego algorytmu, z punktu widzenia teorii mowanie za pomocą szablonów) pozwala zapi- nia. Potęgę dla dowolnej liczby całkowitej do-
automatów są one równoważne maszynie Tu- sywać stałe całkowite w kodzie dwójkowym, co
ringa. Algorytmy te są wykonywane w czasie zwiększa czytelność, jeżeli to bity są istotne.
kompilacji, wyniki ich działania są umieszcza- Szybki start
ne w kodzie wynikowym. Obliczenia w czasie kompilacji Aby uruchomić przedstawione przykłady,
należy mieć dostęp do kompilatora C++
Dostarczając algorytmy, które wykonują się
oraz edytora tekstu. Niektóre przykłady
Zwiększanie czytelności kodu w czasie kompilacji, przyspieszamy działa- korzystają z udogodnień dostarczanych
Jednym z powodów tworzenia metaprogra- nie programów, ponieważ część przetwarza- przez bibliotekę boost::mpl, warunkiem
mów jest chęć zwiększenia czytelności i ela- nia będzie wykonywana w czasie kompilacji, ich uruchomienia jest instalacja biblio-
styczności kodu. Metaprogram tego typu, po- co w pewnych przypadkach zwiększa wiel- tek boost (w wersji 1.36 lub nowszej) Na
kazany na Listingu 1, oblicza wartość funkcji kość kodu. Metaprogramy mogą używać frag- wydrukach pominięto dołączanie odpo-
wiednich nagłówków oraz udostępnianie
silnia podczas kompilacji. Konkretyzacja sza- mentów kodu, a nie tylko stałych, co pokaza- przestrzeni nazw, pełne źródła dołączono
blonu Silnia<N> (gdzie N jest liczbą całkowi- no na przykładzie szablonu Power dostarcza- jako materiały pomocnicze.
tą dodatnią) jest równoważna, dla kodu wyko- jącego funkcji potęgi całkowitej dla argumen-

24 12/2009
Metaprogramowanie

datniej n można uzyskać, wykorzystując po-


kazany powyżej sposób, jeżeli n zapiszemy bi- Listing 1. Metaprogram obliczający wartość funkcji silnia
narnie n=bm*2m + b(m-1)*2(m-1) + ... + b1*2 + b0 template<unsigned int n> struct Silnia {
= (bmb(m-1)...b1b0)2, to xn jest iloczynem skład- static const unsigned int value = n*Silnia<n-1>::value;
ników, które będziemy wielokrotnie podno- };
sić do kwadratu, na przykład x35 = x(32+2+1) = template<> struct Silnia<0> { //specjalizacja, kończy rekurencję
((((x2)2)2)2)2*(x2)*x. Na Listingu 4 przedstawio- static const unsigned int value = 1;
no szablon funkcji Pow, która realizuje przedsta- };
wioną koncepcję, generując w czasie kompilacji unsigned int i = Silnia<0>::value; //przykład użycia, równoważne i = 1
wyrażenie, które oblicza potęgę. i = Silnia<5>::value; //równoważne i = 120
Metaprogramy mogą dostarczać przy-
bliżenia funkcji matematycznych z założo- Listing 2. Metaprogram, który pozwala używać stałych zapisanych binarnie
ną dokładnością. Przykładowo funkcja wy- template <unsigned long n> struct Binary { //stałe binarne
kładnicza może być przybliżana szeregiem static const unsigned long value = Binary<n/10>::value << 1 — n % 10;
};
template <> struct Binary<0> { //stop dla rekurencji
static const unsigned long value = 0;
};
więc dostarczając szablon, można obliczać ją //przykład użycia
z założoną precyzją; patrz Listing 5. const int FLAGS = Binary<1100>::value; //zamiast 12 lub 0xC
Kod tworzony przez metaprogramy często
wykorzystuje się w bibliotekach numerycz- Listing 3. Metaprogram dostarczający funkcji do potęgowania (wykładnik całkowity dodatni)
nych, na przykład do mnożenia czy odwraca- template <unsigned n> inline double Power(double x) {
nia macierzy. Obliczenia wykonywane w czasie return x * Power<n-1>(x); //rekurencyjna definicja funkcji
kompilacji dostarczają stałych lub funkcji, które }
możemy obliczyć lub wyprowadzić za pomocą template <> inline double Power<0>(double x) {
innych narzędzi (na przykład ręcznie), a następ- return 1.0; //specjalizacja kończy rekurencję
nie umieścić je w programie. Stosowanie meta- }
programów zwiększa elastyczność dostarcza-
nych rozwiązań oraz zmniejsza ryzyko popeł- Listing 4. Szablon generuje wyrażenie obliczające potęgę o wykładniku całkowitym
nienia pomyłki, jest też bardziej czytelne, po- template <unsigned n> double Pow(double x) {
nieważ nazwa szablonu zawiera informacje o return Pow<2>( Pow<n/2>(x) )*Pow<n%2>(x);
znaczeniu. }
//specjalizacje dla kwadratu i innych warunków brzegowych
Kolekcje typów template <> double Pow<2>(double x) { return x*x;}
Metaprogramy, których argumentami są ty- template <> double Pow<1>(double x) { return x;}
py, pozwalają wyrażać pojęcia trudne do opisu template <> double int_power<0>(double x) { return 1.0;}
innymi technikami, na przykład pozwala wy-
konywać podobne operacje na grupie wskaza- Listing 5. Szablon generuje wyrażenie obliczające wartość funkcji wykładniczej z założoną
nych typów. W takich przypadkach wykorzy- precyzją
stujemy kolekcje typów, które można utwo- template<unsigned int N> inline double Exp(double x) {
rzyć za pomocą szablonów. Przykład takiej ko- //wykorzystuje szablony z Listingu 1 oraz Listingu 4
lekcji, przechowującej typy w liście jednokie- return Exp<N-1>(x) + Pow<N>(x)/Silnia<N>::value;
runkowej (propozycja opisana w książce An- }
dreia Alexandrescu Nowoczesne projektowanie template <> inline double Exp<0>(double x) {
w C++), pokazuje Listing 6. return 1.0;
Poszczególne elementy listy przechowu- }
ją dowolne typy, natomiast ostatni element

Szablony– przypomnienie
Szablony (templates) dostępne w języku C++ umożliwiają implementację generycznych, to znaczy niezależnych od typów, algorytmów oraz
struktur danych. Przykładowy szablon swap, pokazany poniżej, zamienia zawartość dwu obiektów, możemy go wołać dla dowolnych obiektów
tego samego typu, jeżeli dostarczają one konstruktora kopiującego i operatora przypisania.

template<typename T> void swap(T& a, T& b) {


T tmp = a;
a = b;
b = tmp;
}

Podczas kompilacji następuje konkretyzacja szablonu, co oznacza generowanie kodu dla właściwych typów. Kod generowany na podstawie szablonów
nie różni się od kodu tworzonego ręcznie, nie ma żadnych narzutów pamięciowych i czasowych, jedyną niedogodnością jest dłuższy czas kompilacji.
Specjalizacja to wersja szablonu, która będzie użyta do generacji kodu zamiast wersji ogólnej, gdy parametrami będą odpowiednie typy.

www.sdjournal.org 25
Programowanie C++

jest zawsze typu NullType (lista z wartow- du na konieczność rekurencyjnego zagłębiania la przechowywać dowolną liczbę typów, różnią
nikiem). Za pomocą tak zdefiniowanej li- tych szablonów. Biblioteka boost::mpl dostar- się one pewnymi właściwościami, na przykład
sty możemy utworzyć kolekcję typów o do- cza kolekcji typów oraz operacji, zwalnia ona mpl::set usuwa kolejne wystąpienia tego sa-
wolnej długości, na Listingu 6 pokazano li- programistę z konieczności implementacji wła- mego typu w kolekcji. Najczęściej używa się ko-
stę przechowującą trzy typy: short, int oraz snych rozwiązań. Kolekcje typów dostarczane lekcji mpl::vector, natomiast pozostałe wyko-
long. Listing 7 pokazuje operację obliczania przez tę bibliotekę mają interfejs zbliżony do ze- rzystuje się wtedy, gdy odpowiednie właściwo-
długości kolekcji (liczba elementów listy). stawu metod oferowanych przez kolekcje z bi- ści okażą się przydatne. Dostarczane są operacje
blioteki standardowej, ponadto biblioteka do- modyfikacji tych kolekcji, to znaczy dodawania
boost starcza zbiór przydatnych metafunkcji, pozwa- i usuwania elementów, badania właściwości ko-
metaprogramming library (MPL) lając unikać zapisów rekurencyjnych. Kolekcje lekcji, badania występowania danego typu, sto-
Tworzenie listy typów za pomocą szablonu typów to szablony: mpl::vector, mpl::list, sowania pewnej operacji dla każdego z typów w
TList jest niewygodne i nieczytelne, ze wzglę- mpl::set oraz mpl::map, każdy z nich pozwa- kolekcji, tworzenie nowych typów na podsta-
wie typów przechowywanych w kolekcji. Przy-
Listing 6. Kolekcja typów zdefiniowana rekurencyjnie kład użycia kolekcji został pokazany na Listin-
gu 8. Każda z operacji zakłada, że wynik, któ-
template <class H, class T> struct TList { // lista rekurencyjna ry jest typem, jest składową type, natomiast wy-
typedef H Head; nik, który jest wartością jest dostępny jako skła-
typedef T Tail; dowa type::value. Oczywiście algorytmy wy-
}; konują się w czasie kompilacji.
struct NullType { }; //typ kończący listę Algorytm mpl::for_each jest nietypowy, wo-
//przykładowa kolekcja typów ła on dostarczoną funkcję lub dostarczony funk-
typedef TList<short, TList<int, TList<long, NullType> > > Integers; tor (obiekt klasy, która dostarcza operatora wo-
łania funkcyjnego) dla każdego typu, który jest
Listing 7. Badanie długości listy typów przechowywany w kolekcji. Specyfika tego al-
template <class TList> struct Size { gorytmu polega na tym, że tworzy on kod, któ-
static const int value = Size<typename TList::Tail>::value + 1; ry jest wykonywany w czasie działania, patrz Li-
}; sting 9, gdzie pokazano rozwiązanie (a raczej
template <> struct Size< NullType> { szkic rozwiązania), które wykorzystuje bibliote-
static const int value = 0; kę mpl do rejestracji klas w fabryce obiektów.
}; Biblioteka mpl jest używana przez wiele in-
nych bibliotek dostarczanych przez boost, na
Listing 8. Przykład operacji na kontenerach z biblioteki mpl przykład przez biblioteki tworzące obiekty funk-
typedef mpl::vector<int,char,long,int> Types; // przykładowa kolekcja cyjne bind, bibliotekę dostarczającą variant
typedef mpl::push_back<Types, int>::type NewTypes; // modyfikacja (obiekt, który przechowuje jedną z wybranych
cout << mpl::size<NewTypes>::type::value; // wydrukuje wartość 5 wartości, podobnie jak union w C) czy bibliote-
//algorytm count zlicza wystąpienia danego typu w kolekcji kę spirit, służącą do generowania gramatyk.
cout << mpl::count<Types, int>::type::value; //drukuje liczbę 2
Podsumowanie
Listing 9. Przykład wykorzystania algorytmu for_each z biblioteki mpl Metaprogramowanie umożliwia przetwarza-
//mamy hierarchię klas, gdzie klasą bazową jest Figure nie informacji podczas kompilacji. Techniki
class Square : public Figure { //przykładowa klasa konkretna te pozwalają zmniejszać rozmiar kodu, zwięk-
public: szając jego czytelność, oraz sprawiać, że progra-
//klasa konkretna dostarcza metodę statyczną create my wykonują się szybciej. Metaprogramowa-
static Figure* create() { return new Square; } nie znalazło wiele zastosowań zwłaszcza przy
static int id_; //klasa konkretna przechowuje identyfikator tworzeniu ogólnych, przenośnych i efektyw-
}; nych rozwiązań. Zostanie ono wsparte w no-
struct Factory { //fabryka figur konkretnych, przedstawiony kod bardzo uproszczony wej wersji standardu C++0x.
public:
typedef Figure* (*CreateFig)(); //typ funkcji tworzącej obiekty
static Factory& getInstance(); //dostęp do obiektu fabryki
int registerFig(CreateFig fun); //metoda rejestracji, dostarcza funkcję
W Sieci
tworzącą obiekt, zwraca id • http://www.boost.org – dokumentacja
}; bibliotek boost;
struct RegisterFigure { //szablon użyty do rejestracji • http://www.open-std.org – dokumen-
template<typename T> void operator()(T){ ty opisujące nowy standard C++.
T::id_ = Factory::getInstance().registerFig( T::create );
}
}; ROBERT NOWAK
typedef mpl::vector<Square, Circle, Rectangle> Types; // kolekcja typów dla klas Adiunkt w Zakładzie Sztucznej Inteligencji Instytu-
konkretnych tu Systemów Elektronicznych Politechniki Warszaw-
mpl::for\_each<Types>(RegisterFigure()); //woła w czasie wykonania operację dla skiej, zainteresowany tworzeniem aplikacji dla biolo-
każdego typu gii i medycyny, programuje w C++ od ponad 10 lat.
Kontakt z autorem:rno@o2.pl

26 12/2009
Opis DVD

Programowanie w języku Java wiedni strumień, który następnie można analizować z użyciem wspo-
mnianych wcześniej narzędzi. Kolejny etap kursu jest poświęcony
Od Witaj świecie do aplikacji korporacyjnych. Cz.III wspomnianej klasie URL i jej wykorzystaniu.
Zaawansowana biblioteka standardowa Na koniec pozostał element platformy Java bardzo szeroko wyko-
W trzecim odcinku kursu poznamy zaawansowane elementy języ- rzystywany w aplikacjach typu Enterprise – internacjonalizacja. Po-
ka Java i dostępnych klas. Ponieważ kolekcje odgrywają istotną rolę w znamy podstawowe mechanizmy platformy Java umożliwiające defi-
tworzeniu struktur danych w językach obiektowych, zapoznamy się niowanie wielojęzycznych komunikatów oraz ich wykorzystywanie w
ze sposobami sortowania kolekcji oraz podstawowymi algorytmami, tworzonych aplikacjach.
które możemy wykonywać na kolekcjach.
Kolejną przydatnym narzędziem służącym do realizacji trwałego Programowanie Flex.cz.III
zapisu danych przechowywanych w aplikacji jest serializacja. Pozna- Odcinek 6: W szóstym odcinku, zajmiemy się obsługą bazy danych
my podstawowe konstrukcje związane ze stosowanie serializacji w SQLite oraz kontrolką HTML, podczas tworzenia prostego manage-
języku Java zarówno do postaci binarnej jak i XML. ra kontaktów.
Następny zestaw przydatnych narzędzi dotyczy pracy z dany-
mi tekstowymi. Zapoznamy się z potężnym narzędziem – klasą Delphi© 2010, C++Builder© 2010 (Trial)
Scanner, która ułatwia parsowanie plików i ciągów znakowych do Środowisko Delphi powstało w roku 1995, C++Builder dwa lata póź-
prostych typów danych. Poznamy zastosowanie klasy NumberFormat niej. Pomimo pojawiania się od tamtego czasu wielu nowoczesnych ję-
do formatowania danych liczbowych oraz klas DateFormat i zyków i platform programistycznych biblioteka VCL, komponenty i
SimpleDateFormat do formatowania i parsowania dat. Doskonałym programowanie wizualne nadal zapewniają użytkownikom tych na-
uzupełnieniem tych narzędzi są wyrażenia regularne, które pozwa- rzędzi przewagę w budowie aplikacji okienkowych dla systemu Win-
lają na elastyczną analizę ciągów znakowych. dows, zarówno dwu, jak i wielowarstwowych. Nowe wersje obu środo-
Czasami zachodzi potrzeba odczytania i sparsowania pewnego wisk pozwalają pracować szybciej i tworzyć bardziej innowacyjne roz-
zasobu sieciowego. Z użyciem klasy URL możemy otrzymać odpo- wiązania.

Jeśli nie możesz


odczytać zawartości
płyty DVD, a nie jest ona Redakcja
uszkodzona mechanicznie, nie udziela pomocy
sprawdź ją na co najmniej dwóch technicznej w instalowaniu
napędach DVD. i użytkowaniuprogramów
W razie problemów z płytą, prosimy pisać zamieszczonych na płytach DVD-ROM
pod adres: cd@software.com.pl dostarczonych razem z pismem.

27
Programowanie Java

Przewodnik po SCJP
Czyli certyfikat z Javy – część 1

Proces zdobywania certyfikatów, potwierdzających umiejętności z różnych


dziedzin wiedzy, stał się jednym z ważniejszych elementów osobistego
rozwoju. Proces ten ma miejsce również w branży IT; certyfikaty dla
programistów (Java lub .NET), administratorów czy sieciowców (Cisco)
można coraz częściej odnaleźć w CV osób starających się o pracę.
określa się cykl egzaminów, które pozwala-
Dowiesz się: Powinieneś wiedzieć: ją na zdobycie wybranych egzaminów.
• Czym jest certyfikat SCJP; • Jak skompilować i uruchomić programy w Z reguły, aby zdawać certyfikaty wyż-
• Co zawiera materiał egzaminacyjny; Javie; szych poziomów, trzeba posiadać certyfika-
• Jak prawidłowo deklarować i inicjalizować • Jakie są podstawy składni języka; ty niższego poziomu. W przypadku Javy re-
klasy i zmienne (poznasz zagadnienie 1). • Czym różni się klasa od interfejsu (m.in.). lacja jest prosta – jeden egzamin, jeden cer-
tyfikat, aczkolwiek niektóre firmy (zwłasz-
cza w przypadku certyfikatów wyższego
poziomu) wymagają zdania dwóch lub wię-
Dlaczego akurat certyfikat? cej egzaminów.
Zdobywanie kolejnych certyfikatów daje Ścieżka certyfikacyjna została przedsta-
Poziom dwie poważne korzyści: wiona na Rysunku 1. Pierwszy, najniższy
trudności certyfikat – SCJA (Sun Certified Java Asso-
• wpis do CV, który wraz z pewną (nawet ciate) ma jedną istotną wadę, która z pew-
niedużą) dozą doświadczenia wypada nością zmniejsza liczbę osób podchodzą-
niezwykle korzystnie w oczach poten- cych do adekwatnego egzaminu – certyfi-

S
amo wykształcenie informatyczne cjalnych pracodawców; kat ten nie jest konieczny do zdawania cer-
często nie wystarcza. Główny po- • poszerzenie swoich umiejętności w za- tyfikatu wyższego rzędu. Z tego względu w
wód jest związany z dość ogólnym kresie materiału danego certyfikatu. tym cyklu artykułów nie będziemy się nim
charakterem studiów informatycznych w w ogóle zajmować.
Polsce; konieczność przestrzegania usta- Co do pierwszego podpunktu, to nie trze- W centrum naszego zainteresowania znaj-
wowych zapisów zmusza uczelnie do na- ba chyba specjalnie dyskutować, kontro- dzie się certyfikat SCJP (Sun Certified Java
uczania studentów różnych przedmio- wersje może wzbudzać ten drugi, zwłasz- Programmer) w wersji dla Javy 6. Po zdaniu
tów informatycznych, niezależnie od fak- cza dla osób doświadczonych. Ja? Certyfi- tego certyfikatu można przystąpić do jed-
tycznych zainteresowań studentów. Ma kat? Przecież programuję w Javie od przedwoj- nego z pięciu różnych certyfikatów – jed-
to oczywiście swoje dobre strony (o czym nia! – z pewnością w ten czy podobny spo- nego dla platformy Java SE (SCJD – SC Ja-
autor artykułu przekonał się nieraz oso- sób zareagowałoby wielu programistów ma- va Developer), trzech dla Javy EE (SC Web
biście), ponadto w obrębie wielu kierun- jących doświadczenie w Javie na propozycję Component Developer, Business Compo-
ków istnieją specjalizacje, jednak gdy ab- zdawania egzaminu certyfikującego. Prawda nent Developer, Developer for Java Web Se-
solwent niezłej uczelni, nawet z dobrym jest jednak o wiele bardziej bolesna – znajo- rvices) i jednego dla Javy ME (SC Mobile
ocenami, staje przed osobą z działu HR mość danej technologii czy języka dość czę- Application Developer). Zdanie dowolnego
prowadzącą postępowanie rekrutacyjne, sto ogranicza się do najczęściej wykorzysty- z tych egzaminów pozwala na podejście do
pada zasadnicze pytanie – co Pan(i) fak- wanych konstrukcji czy mechanizmów. Bar- ostatniego egzaminu – SC Enterprise Archi-
tycznie umie? dzo często nie wiemy, co w ten sposób traci- tect, najbardziej skomplikowanego, w przy-
Zdobywanie doświadczenia to jedno, my. Piszę o tym na podstawie własnych do- padku którego sama wiedza to zdecydowa-
jednak nawet osoby z dużym doświadcze- świadczeń – miałem wrażenie, że po moich nie za mało – potrzebne jest jeszcze niema-
niem z rozmaitych technologii mogą (zu- różnych Javowych perypetiach znałem cał- łe doświadczenie. Ścieżka certyfikacyjna jest
pełnie nieświadomie) nie znać stosunko- kiem nieźle. Niestety, myliłem się. przedstawiona na Rysunku 1.
wo prostych i podstawowych pojęć z ni-
mi związanych. Certyfikaty, a Java Forma egzaminów
W tym momencie do gry wkraczają cer- Java, podobnie jak wiele zaawansowanych Egzaminy SC* są w większości przypadków
tyfikaty. Po co właściwie zawracać sobie technologii informatycznych, oferuje wła- przeprowadzane w formie testów (tak wła-
nimi głowę? sną ścieżkę certyfikacyjną. Mianem tym śnie ma to miejsce w przypadku egzaminu

28 12/2009
Certyfikat SCJP

SCJP). Test składa się z pytań wielokrotne- ucher. Jego cena w Polsce jest jednolita dla Jeśli więc jesteś osobą przygotowującą
go wyboru (aczkolwiek zdający otrzymuje wszystkich certyfikatów i wynosi 750 zł. się do egzaminu – prawdopodobnie znaj-
podpowiedź w postaci liczby poprawnych Po zakupieniu vouchera możesz udać się do dziesz tu kilka cennych wskazówek. Jeśli
odpowiedzi) i pytań typu drag'n'drop, po- wybranego centrum Prometric i umówić się zastanawiasz się nad podejściem do egza-
legających na przeciąganiu fragmentów ko- na egzamin. minu – artykuły pozwolą Ci na zrozumie-
du na odpowiednie miejsca. Oczywiście od nie tych dość specyficznych klocków. Jeśli
momentu pisania tego artykułu do jego pu- Przewodnik != kurs zaś certyfikat już masz lub jesteś po pro-
blikacji forma egzaminu może ulec zmia- Niniejszy cykl artykułów absolutnie nie ma stu programistą Javy – w tym cyklu arty-
nie. Dotyczy to zwłaszcza liczby pytań i charakteru pełnego, kompletnego kursu Ja- kułów odnajdziesz po prostu czystą Javową
progu zdawalności. Obecnie należy odpo- vy przygotowującego do egzaminu. W tym wiedzę, która może okazać się przydatna w
wiedzieć poprawnie na 35 z 60 pytań (próg celu zostały napisane doskonałe książki (ta- różnych sytuacjach.
58,3%). kie jak Sun Certified Programmer & Deve- Aby wprowadzić pewien porządek do
loper for Java 5 Study Guide (Exam 310- omawianych zagadnień (materiału jest
Jeszcze trochę o formalnościach 055)), więc nie ma sensu ich dublować. wszak niezmiernie dużo), będę omawiał ko-
Gdy poczujesz, że Twoje przygotowania do Warto jednak zwrócić uwagę na pewne za- lejne podpunkty (w ramach wyżej wymie-
egzaminu zostały zakończone, musisz się na gadnienia, które szczególnie często poja- nionych zagadnień), opisane na stronie http:
niego zarejestrować. Egzaminy firmy Sun wiają się na egzaminie; ich właściwe zrozu- //www.sun.com/training/catalog/courses/CX-
mogą być przeprowadzane w centrach Pro- mienie pozwoli Wam na uzyskanie znacz- 310-065.xml, nazywane też zadaniami (ang.
metric w całej Polsce. Na początku musisz nie lepszego wyniku. objective).
jednak zdobyć voucher – specjalny kupon W każdym kolejnym artykule będziemy
egzaminacyjny. W praktyce jest to specjal- prezentować omówienie jednego z siedmiu Do pracy, rodacy – od
ny e-mail, zawierający numer, który musisz głównych zagadnień wchodzących w skład deklaracji wszystko się zaczyna!
potem podać w centrum egzaminacyjnym materiału do egzaminu SCJP dla Javy SE 6 Na dobry początek co nieco o podstawo-
przy zapisach na egzamin. Aby zdobyć ta- (CX-310-065). wych i najprostszych, wydawałoby się, kon-
ki voucher, musisz zarejestrować się na stro- strukcjach, jakie znajdziemy w Javie – dekla-
nach firmy Sun. I tu mała ciekawostka dla • Declarations, Initialization and Scoping; racjach, blokach inicjalizacyjnych i zasięgach
studentów, a także pracowników akademic- • Flow Control; klas i interfejsów. Teoretycznie – nic trudne-
kich – część egzaminów firmy Sun (w przy- • API Contents; go, w praktyce na egzaminie bardzo rzadko
padku programistów są to egzaminy SCJA, • Concurrency; mamy do czynienia z przejrzystymi, czytel-
SCJP i SCWCD) została objęta programem • OO Concepts; nymi i prostymi fragmentami kodu – mo-
SAI – Sun Academic Initiative, dzięki które- • Collections/Generics; żesz o tym zapomnieć!
mu koszt voucherów na te egzaminy wyno- • Fundamentals. Nie będziemy zajmować się standardo-
si 40 dolarów! Więcej informacji znajdziesz wymi przykładami deklaracji klas czy inter-
na stronie http://www.sun.com/solutions/ Warto zwrócić uwagę, że język Java został fejsów, dlatego przejdziemy do najciekaw-
landing/industry/education/sai/index.xml. Je- ujęty w trzech, czterech zagadnieniach – na- szych (czytaj: najbardziej paskudnych), mo-
śli jednak nie masz już żadnych związków tomiast treści dotyczące API, wielowątkowo- im skromnym zdaniem, rodzajów zadań. Za-
z uczelnią, musisz zakupić tradycyjny vo- ści czy kolekcji wchodzą ewidentnie w skład danie 1.1 obliguje nas do zajęcia się nie tyl-
funkcjonalności Javy SE, ME i EE (nie nale- ko klasami czy interfejsami, ale także typami
Listing 1. Błąd w deklaracji typu żą zaś do języka). wyliczeniowymi – enum.
wyliczeniowego

enum Pierwszy {raz, dwa }; �����������������������������������������


class Klasa {
enum Drugi { trzy, cztery}; �������������
������������� ������������� ������������� �������������
������������� ������������������ ������������� ������������������
public void metoda() { ������ ��������������
��������� ��������� ����������������� ���������
������
enum Trzeci { piaty, szosty }; ������� ������� �������� �������

}
} ������������ ������������������������������������

Listing 2. Rozszerzona, ale jak najbardziej ��������� �����������������������������������


poprawna deklaracja typu wyliczeniowego
��������������������������� ����������

enum Ksztalt {
Trojkat, Kwadrat, Romb; Rysunek 1. Ścieżka certyfikacyjna dla programistów języka Java (ze strony http://www.sun.com/
public void metoda() { training/certification/java/scjp.xml).
System.out.println("Tak,
w enumie mogą być metody "
+ this.k + ", a Z ostatniej chwili!
także pola!"); Niedługo przed zakończeniem pisania tego artykułu pojawiły się informacje na temat betate-
stów nowej wersji egzaminu – SCJP Plus. Większy nacisk zostanie położony na kwestie prak-
}
tyczne – konieczne będzie np. samodzielne pisanie kodu. Nie zmienia to jednak faktu, że za-
int k = 3; dania (objectives) pozostają takie same – zakres wiedzy koniecznej do zdania egzaminu nie
} ulegnie więc zmianie.

www.sdjournal.org 29
Programowanie Java

Wyliczenia, czyli swojski enum Oczywiście nieco bardziej zaawansowa- Oto nieco zmodyfikowany, poprzedni
Na pierwszy rzut oka typ wyliczeniowy może ny czytelnik zwróci uwagę, że już po- przykład – Listing 3.
wydawać się niezwykle prosty: przedni przykład zaburza nieco tę regu- Ot i cała tajemnica. Zwróć uwagę, że war-
łę – normalne klasy mogą być deklarowa- tości typu są tworzone za pomocą nowe-
enum Kolory { czerwony, niebieski, zielony ne wewnątrz metod (lokalnie). Jest to je- go konstruktora. Co za tym idzie, warto-
}; den z nielicznych wyjątków, który odróż- ści te możemy traktować tak, jak gdyby by-
nia konstrukcję enum od tradycyjnych klas. ły publicznymi, statycznymi i niezmienny-
Już taka deklaracja tworzy pełnoprawny, W praktyce, deklaracje typów wyliczenio- mi polami.
funkcjonalny typ wyliczeniowy. Do tego do- wych pod względem składni mogą wyglą- Na tym kończymy nasze wyliczeniowe
łączamy prostą obsługę: dać dużo bardziej egzotycznie w porówna- rozważania. Na zakończenie polecam zwró-
niu z tymi, z którymi mieliśmy do czynie- cić uwagę na możliwość deklaracji podklasy
Kolory kolory = Kolory.czerwony; nia do tej pory. dla każdej z wartości typu wyliczeniowego
(sic!). Ma to wiele wspólnego z klasami ano-
I już możemy się cieszyć wyliczeniami w na- Enum z rozmachem nimowymi, dlatego nie będziemy tym zajmo-
szej aplikacji. Skoro typy wyliczeniowe przypominają w bu- wać się teraz.
Zadania egzaminacyjne mogą nas jednak dowie klasę, nie powinna Cię zdziwić nastę-
sprawdzać pod kątem dwóch irytujących pująca konstrukcja – Listing 2. (import static),
cech wyliczeń: możliwych miejsc ich deklara- Jak widać, najpierw następuje deklaracja (import static), (import static)...
cji (proste), a także ich budową (trochę trud- możliwych wartości typu wyliczeniowego, a Powyższy śródtytuł powinien w zasadzie
niejsze). Zacznijmy od prostej reguły: następnie dodatkowe elementy klasy – me- wystarczyć Ci do zapamiętania jednej z
Typ wyliczeniowy może być zadeklarowa- tody, pola, bloki inicjalizacyjne itd. dwóch ważnych reguł związanych z impor-
ny na zewnątrz klasy, wewnątrz klasy (tam, Enumy mają jeszcze kilka istotnych towaniem statycznym – kolejności tych
gdzie pozostałe pola klasy), ale nigdy, prze- cech. Do najważniejszych należy kwestia dwóch słów kluczowych. Jest to mecha-
nigdy wewnątrz metody! Co za tym idzie, konstruktora. Otóż jego deklaracja jest nizm, który działa analogicznie jak impor-
poniższy kod – Listing 1. – zawiera błąd możliwa, jednak jedynymi dopuszczalny- towanie zwykłe. W przypadku tradycyjnej
w wierszu czwartym – w deklaracji typu mi modyfikatorami dostępu są private instrukcji import możemy odwoływać się
Trzeci. Pozostałe deklaracje są jak najbar- i domyślny! do elementów pakietów bez konieczności
dziej poprawne. W praktyce niemożliwe jest własno- podawania pełnej nazwy danego elementu
Nadszedł czas na nieco bardziej skompli- ręczne tworzenie nowych wartości typu – Listing 4.
kowany aspekt typów wyliczeniowych. Regu- wyliczeniowego za pomocą konstrukto- Importowanie statyczne pozwala na odwo-
ła nr 2 brzmi: ra. Utworzenie konstruktora ma jednak ływanie się do statycznych składników danej
znamienny wpływ na deklarację wartości klasy (zarówno pól, jak i metod) bez koniecz-
Typy wyliczeniowe działają jak klasy. enuma. ności podawania jej nazwy. Oto najprostszy
przykład:
Listing 3. Typ wyliczeniowy z konstruktorem
import static java.lang.Integer.MAX_VALUE;
enum Ksztalt {
Trojkat("trójkat"), Kwadrat("kwadrat"), Romb("romb"); Ta instrukcja (umieszczona pośród innych
private Ksztalt(String s) { importów) pozwoli nam na odwoływanie
ksz = s; się do pola MAX _ VALUE tylko za pomocą je-
} go nazwy:
public String toString() {
return "Mój kształt to: " + ksz; System.out.println(MAX_VALUE);
}
String ksz; Jeśli chcesz zimportować wszystkie ele-
} menty statyczne z danej klasy – skorzystaj
z gwiazdki:
Listing 4. Dwa różne sposoby podawania nazwy typu
import java.util.List; import static java.lang.Integer.*;
....
List<String> lista = .... Przygody z interfejsami
zamiast Interfejsy same w sobie stosunkowo rzadko
java.util.List<String> lista = ... pojawiają się na egzaminie, przynajmniej w
tym zadaniu. Należy jednak pamiętać o kil-
Listing 5. Uwaga na modyfikatory dostępu! ku podstawowych zasadach. Niech nigdy nie
class Klasa2 implements Interfejs1 { zmyli Cię zestaw modyfikatorów występu-
void metoda() {} // BŁĄD! jących przy metodach i polach interfejsów.
} Zwykła deklaracja interfejsu:
interface Interfejs1 {
void metoda(); interface Interfejs1 {
} int pole = 1;
void metoda();
}

30 12/2009
Certyfikat SCJP

jest równoważna zapisowi następującemu: czniemy od najprostszej, ale jednocześnie nie- podania litery f za liczbą (TAK: float f
zbędnej do zapamiętania reguły – zasad two- = 30.5f;, NIE: float f = 30.5;)
interface Interfejs1 { rzenia identyfikatorów w Javie. • inicjalizacja łańcuchów za pomocą samej
public static final int pole = 1; Poprawny identyfikator może rozpoczy- wartości z punktu widzenia JVM ma in-
public void metoda(); nać się od dowolnej litery, znaku podkreśle- ny efekt, niż korzystanie z konstruktora
} nia (_) i znaku dolara ($), natomiast wśród ko- (String s = "tekst" to nie to samo co
lejnych znaków, poza już wymienionymi, mo- String s = new String("tekst")) –
Powyższa swoboda niesie ze sobą konsekwen- gą znaleźć się cyfry. Chciałbym zwrócić uwa- więcej na ten temat znajdziesz w jednym
cje w postaci dwóch dodatkowych typów za- gę zwłaszcza na znak dolara – jest on charak- z kolejnych artykułów.
dań. Istotą pierwszego z nich jest określenie terystyczny dla uniksowych języków skrypto- • nawiasy kwadratowe niezbędne do de-
poprawnych modyfikatorów przy polach in- wych oraz języka PHP. klaracji zmiennych tablicowych mogą
terfejsu. Należy pamiętać, że w deklaracji Po tym krótkim, acz ważnym wstępie mo- znaleźć się zarówno przed, jak i za na-
pola mogą znaleźć się dowolne z trzech słów: żemy przejść do spraw bardziej złożonych. zwą zmiennej (int[] tab i int tab[]
public, static, final i ich kombinacje. Inicjalizacja zmiennych nie powinna spra- są poprawnymi zapisami). Tak samo
Drugi typ zadań jest równie prosty – mu- wiać większych problemów, jednak radzę za- wygląda sytuacja w przypadku parame-
sisz pamiętać, że klasa implementująca in- pamiętać sobie kilka ważnych reguł: trów metod i wszelkich innych deklara-
terfejs nie może określić innych modyfika- cji zmiennych tablicowych.
torów dostępu dla metod interfejsu! Poniż- • literały zmiennoprzecinkowe są typu • naprawdę często na egzaminie występu-
szy przykład – Listing 5. jest zły! Brakuje sło- double, zaś całkowite – typu int. O ile ją zadania zawierające bezpośrednie ini-
wa kluczowego public przed implementa- jednak liczby całkowite można przypi- cjalizacje tablic.
cją metody. sywać bez konwersji do zmiennych ty-
Ostatnia kontrowersyjna kwestia ma zwią- pów o mniejszym rozmiarze (np. byte b Bezpośrednia inicjalizacja tablicy może prze-
zek z duplikowaniem metod i pól. Chodzi o = 30;), o tyle przypisanie literału zmien- biegać dwojako – z wykorzystaniem słowa
konflikty, które mogą mieć miejsce w przy- noprzecinkowego do typu float wymaga kluczowego new i bez:
padku implementowania dwóch lub więcej
interfejsów, określających metody/pola o tej Listing 6. Przeciążanie funkcji nie wpływa na implementacje metod z interfejsów
samej nazwie.
Rozwiązanie problemu w przypadku me- class Klasa implements Interfejs1, Interfejs2 {
tod jest proste – jeśli metody z dwóch inter- public void metoda(int k) { }
fejsów mają takie same nazwy i listy argu- public void metoda(String s) { }
mentów, wystarczy zaimplementować jedną }
metodę, której nagłówek jest zgodny z tym interface Interfejs1 {
pochodzącym z interfejsów. Uwaga! Wszel- void metoda(int k);
kie wyjątki rzucane przez metody z obu in- }
terfejsów (klauzula throws), w przypadku interface Interfejs2 {
gdy są one różne, można po prostu pominąć void metoda(String s);
– jest to zgodne z regułami języka. Problemu }
z metodami nie ma oczywiście, gdy z punk-
tu widzenia klasy są to metody przeciążone (z Listing 7. Typ zwracanej wartości nie wystarcza do odróżniania metod
ang. Overloaded) – Listing 6. class Klasa implements Interfejs1, Interfejs2 {
Problem pojawia się, gdy nasze metody róż- public String metoda() { return ""; }
nią się jednym elementem – typem zwraca- public Integer metoda() { return 0; }
nej wartości. Poniższy zapis spowoduje dwa }
błędy kompilacji – Listing 7. interface Interfejs1 {
Na szczęście tego typu sytuacje mają miej- String metoda();
sce rzadko; musisz jednak uważać i jeśli w }
którymś z zadań znajdziesz podobny frag- interface Interfejs2 {
ment kodu, powinieneś poważnie zastano- Integer metoda();
wić się nad zaznaczeniem odpowiedzi Com- }
pilation fails.
W przypadku implementowania interfej- Listing 8. Stałe o powtarzających się nazwach to bomba z opóźnionym zapłonem
sów, które deklarują dwa takie same pola, class Klasa implements Interfejs1, Interfejs2 {
sytuacja jest nader ciekawa – błędów kom- public String toString() {
pilacji nie będzie dopóki, dopóty nie spró- return STALA; // BŁĄD!
bujesz skorzystać niejawnie z pola – Li- }
sting 8. }
Jeśli jednak musisz uzyskać dostęp do sta- interface Interfejs1 {
łych, wystarczy odwołać się do nich jawnie, np. int STALA = 2;
korzystając z konstrukcji Interfejs1.STALA. }
interface Interfejs2 {
Czas na zmienne! int STALA = 3;
Deklaracja i inicjalizacja zmiennych zajmu- }
ją istotne miejsce w ramach zadania 1.3. Za-

www.sdjournal.org 31
Programowanie Java

int[][] tab = new int[][] { {3,5}, {4}}; możemy uzyskać do niej dostęp (oczywi- że wszystkie literały całkowite są typu int!
int[][] druga = { {3, 5}, {4}}; ście nie mówimy tutaj o widoczności ele- W związku z tym zostanie wywołany drugi
mentów klas, chodzi raczej o czas jej ży- wariant metody.
Nie daj się zmylić! Jeśli zawartość tablicy de- cia). Najszerszy zakres (a zatem i najdłuż- Czas na największe problemy związane
klarujesz w momencie jej tworzenia, nigdy, szy czas życia) mają zmienne statyczne. z przeciążaniem. Duże komplikacje powo-
ale to przenigdy nie możesz określać jej wy- Zmienne takie rozpoczynają swój żywot w dują tzw. wrappery – klasy opakowujące
miarów w nawiasach kwadratowych! Jeśli momencie załadowania klasy. Dzieje się to typy prymitywne w Javie. Czasami istnie-
zatem zobaczysz zapis: w wielu różnych przypadkach, niemniej za- je konieczność skorzystania z wartości ty-
wsze następuje to przed utworzeniem kon- pu prymitywnego jak z obiektu – np. gdy
new int[2][] { {3}, {5,7}}; kretnych instancji klasy. Zmienne statycz- chcemy dodać go do jakiejkolwiek kolek-
ne są dostępne z poziomu wszystkich eg- cji. Sama wartość nie może być doń doda-
możesz śmiało wykluczyć daną odpowiedź zemplarzy klasy. na, dlatego konieczne jest opakowanie jej
ze zbioru poprawnych odpowiedzi. Ze zmiennych instancji można korzy- w obiekt stosownej klasy. W ten sposób dla
Na zakończenie tego akapitu ciekawost- stać podczas życia pojedynczego obiektu, typu int mamy klasę Integer, dla bool
ka (przynajmniej dla niektórych). Jak są- zmienne lokalne (automatyczne) są dostęp- – Boolean itd. Od Javy w wersji 5 operacje
dzisz, ile wymiarów muszą mieć tablice, ne od momentu ich deklaracji do zakończe- przekształcania wartości na obiekty są wy-
aby mogły być wskazywane przez zmien- nia działania metody, w której zostały zade- konywane automatycznie – wcześniej trze-
ne tab1 i tab2? klarowane, zaś zmienne blokowe – jedynie ba było dokonywać tego ręcznie, np. za po-
w obrębie bloku. Oto mały przykład – Li- mocą stosownych metod.
int[] tab1, tab2[]; sting 9. Tyle tytułem wstępu – jak to się jednak od-
Typowe pytania sprawdzają, czy zdający nosi do naszego przeciążania? Otóż jednym
Pierwsza z nich jest oczywiście jednowy- potrafi wykryć sytuacje, w których niektóre z ulubionych wariantów zadań jest umiesz-
miarowa, ale co z drugą? Podpowiedź: za- zmienne są nieosiągalne, np.: czanie wśród przeciążonych metod zarów-
kryj ręką (a raczej palcem) fragment zawie- no tych zawierających typy prymitywne, jak
rający zmienną tab1. Zobaczysz wtedy cał- • użycie zmiennej blokowej poza jej blo- i wrappery:
kiem poprawną deklarację dwuwymiaro- kiem;
wej zmiennej tablicowej: • użycie zmiennej instancji w metodzie void metoda(int i) { System.out.println("i
statycznej (zwróć uwagę, że odwrotna nt"); }
int[] tablica[]; sytuacja jest jak najbardziej poprawna, a void metoda(Integer i) { System.out.printl
często nawet pożądana!) n("Integer"); }
Zasięg (czas życia) zmiennych void test() { metoda(30); }
Kolejne zagadnienie zmusza nas do inne- Overload
go spojrzenia na kwestie zmiennych. Nie li- – ciężkie przeciążanie Ten przypadek jest prosty – literał 30 jest ty-
czy się, co przechowuje zmienna, lecz gdzie Nadszedł czas na jedno z najciekawszych pu int, więc w takim przypadku dopasowa-
(i moich ulubionych) zagadnień – zada- nie metody jest oczywiste.
Listing 9. Zasięg działania zmiennych w nie 1.4, czyli przesłanianie (ang. overri- Problem może pojawić się w sytuacji na-
praktyce de) i przeciążanie metod. Są to zagadnie- stępującej:
nia związane również z programowaniem
class Zmienne { obiektowym, dlatego przesłanianie poru- void metoda(long i) { System.out.println("
static int statyczna = 0; szę w piątym artykule cyklu (zagadnienia long"); }
int instancji = 0; programowania obiektowego); natomiast void metoda(Integer i) { System.out.printl
void metoda() { w tym miejscu skupię się najbardziej na n("Integer"); }
int lokalna; prawidłowym rozwiązywaniu zadań z prze- void test() { metoda(30); }
while (true) { ciążania metod.
int blokowa; W tym momencie mam dla Ciebie dwie Co z punktu widzenia JVM ma więk-
} wiadomości: dobrą i złą. Dobra jest ta- szy priorytet – czy dopasowanie do pa-
} ka, że idea zadań związanych z przeciąża- sującego typu prymitywnego, czy do kla-
} niem jest prosta – kod zawiera kilka defi- sy bezpośrednio związanej z najwłaściw-
nicji metod przeciążonych, a także kilka szym typem prymitywnym? Twórcom
Listing 10. Subtelne różnice w przeciążaniu wywołań. Musisz, rzecz jasna, wybrać ze- Javy zależy w dużej mierze na kompaty-
metod
staw metod, które faktycznie zostaną wy- bilności. Programiści, którzy korzystali
void metoda(short s) { wołane. Oto jeden z prostszych przykła- ze starszych wersji JVM, mogli polegać
System.out.println(„short"); dów – Listing 10. na automatycznym wywoływaniu metod
} Jaki będzie efekt wywołania metody o pasujących typach prymitywnych (jak
void metoda(int i) { test()? W pierwszym wywołaniu przeka- int->long). Tego samego nie da się po-
System.out.println(„int"); zujesz zmienną typu short, dlatego nie po- wiedzieć o wrapperach, ponieważ auto-
} winno zdziwić wywołanie pierwszej z metod. matyczne opakowywanie wartości pry-
void test() { Drugi przypadek jest bardziej skomplikowa- mitywnych pojawiło się dopiero w Ja-
short s = 30; ny. Teoretycznie liczba 30 mieści się w zakre- vie 5. Co za tym idzie – long wygrywa
metoda(s); sie obu typów. Chcąc skorzystać z najbardziej z Integerem . Operację automatycznego
metoda(30); „oszczędnego” typu, można by przypusz- dopasowywania wartości do bardziej po-
} czać, że zostanie uruchomiony pierwszy wa- jemnego typu nazywamy poszerzaniem
riant – nic bardziej mylnego! Nie zapominaj, (ang. widening).

32 12/2009
Certyfikat SCJP

Poszerzanie dotyczy jednak tylko typów • metoda może zawierać tylko jeden class Bazowa {
prymitywnych; nie można go automatycznie zmienny argument; Bazowa(int x) { }
przenieść w świat wrapperów! Poniższy kod • przy dopasowywaniu przeciążonych }
nie skompiluje się: metod warianty zawierające argumen- class Podklasa extends Bazowa { }
ty zmienne są używane w ostateczności
void metoda(Long i) { System.out.println(" (z tej samej przyczyny, z której typ long Mogłoby się wydawać, że powyższy kod po-
Integer"); } był stosowany przed Integerem – kom- winien bez przeszkód się skompilować. Nie-
void test() { metoda(30); } patybilność!). stety, kompilator dołączy do klasy Podklasa
następujący konstruktor:
Wynika to z faktu, że literał typu int nie W przykładowych zadaniach znajdziesz za-
może być automatycznie przekonwerto- danie poświęcone przeciążeniom metod – public Podklasa() { super(); }
wany do obiektu klasy Long. Gdyby nawet przeanalizuj je uważnie!
kompilator był w stanie domyślić się i utwo- Oczywiście w klasie Bazowa brakuje bez-
rzyć obiekt klasy Integer (operacja pako- Konstruktorowe zagwozdki parametrycznego konstruktora, stąd błąd
wania – ang. boxing), nie jest możliwe auto- Zadanie 1.5. jest ściśle związane z kon- kompilacji. Czasami w ramach zadania na
matycznie zrzutowanie go do obiektu klasy struktorami – specjalnymi mechanizma- wstępie dowiesz się, że w zadaniu tkwi błąd
Long – nie występuje między nimi taka sa- mi dostępnymi w obrębie klas, które po- – a Twoim celem będzie wybranie popra-
ma relacja, jak pomiędzy obydwoma typami zwalają na tworzenie egzemplarzy (instan- wek, umożliwiających naprawienie błę-
prymitywnymi! cji) klas. du. W takiej sytuacji z reguły można zmie-
Na zakończenie rozważań o przeładowa- Abstrahując od licznych dyskusji po- nić dwa fragmenty kodu – dodać adekwat-
niu jedna z moim ulubionych konstrukcji święconych np. zastosowaniom konstruk- ny konstruktor w klasie bazowej lub zade-
– varargs, czyli zmienne argumenty. torów (wbrew pozorom zbyt częste stoso- klarować jawnie konstruktor w klasie po-
Konstrukcja ta jest stosowana w argumen- wanie konstruktorów w kodzie nie jest do- chodnej.
tach metod. Zastosowanie jej jako argumen- brym rozwiązaniem; jeśli interesuje Cię Na zakończenie omawiania konstrukto-
tu metody pozwoli na wywołanie danej me- ten temat, poszukaj informacji związanych rów drobna uwaga. W Javie nie jest zabro-
tody z dowolną liczbą argumentów, oczywi- z wzorcami projektowymi, takimi jak bu- nione deklarowanie metod o takiej samej na-
ście w miejscu podania zmiennego argumen- downiczy, fabryka abstrakcyjna, czy meto- zwie, co nazwa klasy! Oczywiście metody ta-
tu. Opis jest może dość dziwaczny, ale sens da wytwórcza), Java zmusza nas do zapo- kie nie są konstruktorami, niemniej poniższy
istnienia tego mechanizmu doskonale odda- znania się z interesującymi zasadami ich kod skompiluje się:
je metoda format(): działania.
O konstruktorze w kontekście pojedyn- class AAA {
System.out.println(String.format("Arg czej klasy powiedzieć wiele nie można. Pro- public AAA() {}
ument 1: %s, i blemy pojawiają się, gdy w ramach które- public void AAA() {}
drugi: %s", "nr1", goś z zadań zobaczysz kod kilku klas, po- }
"nr2")); wiązanych ze sobą za pomocą dziedzicze-
nia. Wtedy objawia się cały urok konstruk- Słówko new, a klasy
Statyczna metoda format() klasy String torów, który można zawrzeć w dwóch pro- zagnieżdżone i wewnętrzne
działa analogicznie jak funkcja printf() z ję- stych regułach: Na koniec rozważań o konstruktorach
zyka C++ – pozwala na podanie łańcucha za- i tym samym analizy problemów związanych
wierającego miejsce na argumenty, a następ- • Pierwsza instrukcja każdego z jaw- z pierwszym zagadnieniem zajmiemy się wy-
nie listy tych argumentów, przekazywanych nie zadeklarowanych konstrukto- korzystaniem prostego (z reguły) słowa klu-
po przecinku, przy wywołaniu metody. rów musi zawierać wywołanie któ- czowego – new.
Aby zadeklarować zmienny argument w regoś z konstruktorów klasy bazo- Utworzenie egzemplarza zwykłej klasy za-
nagłówku metody, należy skorzystać z zapi- wej (np. super()) lub tej samej klasy leży jedynie od tego, czy została ona zimpor-
su typ ... nazwa, np.: (np. this()). W przypadku pominię- towana. Mamy więc do czynienia z dwoma
cia wywołania, automatycznie zosta- zapisami:
void wyswietlArgumenty(String ... nie wstawione wywołanie bezparame-
argumenty) { trycznego konstruktora klasy bazowej java.util.List<String> lista = new java.ut
for (int i=0;i<argumenty.length;i++) – super()!). il.List<String>();
System.out.println(argumenty[i]); • Jeśli klasa nie zawiera zadeklarowanych
} konstruktorów, kompilator automatycz- lub
nie wstawia publiczny konstruktor bez-
Jak widać, wewnątrz metody argument parametryczny, zawierający domyślne, List<String> lista = new List<String>();
zmienny jest widziany identycznie jak ta- bezparametryczne wywołanie konstruk-
blica danego typu. Będzie to miało też tora klasy bazowej. Problem pojawia się, gdy chcesz utworzyć
swoje konsekwencje przy przeciążaniu obiekt klasy wewnętrznej, czyli klasy zdefi-
metod. Te dwie zasady nie są skomplikowane; skąd niowanej wewnątrz innej klasy (o ile ma od-
Zadania ze zmiennymi argumentami wy- zatem tyle problemów z tego typu zadania- powiedni modyfikator dostępu i nie jest sta-
magają zapamiętania kilku kolejnych przy- mi? Problem polega na tym, że w trakcie tyczna – o niej za chwilę). W takim przy-
datnych reguł: analizy kodu musisz czasem pamiętać o in- padku utworzenie nowego obiektu wymaga
strukcjach, których nie ma na listingu – a są posiadania instancji klasy zewnętrznej. Za-
• zmienny argument musi być umieszczo- generowane przez kompilator! Przeanalizuj łóżmy, że dysponujemy następującymi kla-
ny na końcu listy argumentów; poniższy przykład: sami:

www.sdjournal.org 33
Programowanie Java

Pytania
1. W pliku main.java znajduje się następujący kod:

public class Main {


public static void main(String[] args) {
Gatunki g = Gatunki.ROCK;
System.out.println(g);
}
}
public enum Gatunki {
JAZZ("Jazz"), ROCK("Rock"){
public String toString() {
return "A ja lubię cięższe granie!";
}
} // 1
TECHNO("Techno"); // 2
private Gatunki(String s) { // 3
this.nazwa = s;
}
public String toString() {
return "Mój ulubiony gatunek to " + nazwa;
}
private String nazwa;
}

Wybierz prawidłową opcję:

a) Program wyświetli tekst A ja lubię cięższe granie!;


b) Program nie skompiluje się z powodu błędu w miejscu nr 1;
c) Program nie skompiluje się z powodu błędu w miejscu nr 2;
d) Program nie skompiluje się z powodu błędu w miejscu nr 3;
e) Program nie skompiluje się z powodu błędów wielu miejscach, niekoniecznie oznaczonych cyframi.

2. Jaki efekt otrzymasz po uruchomieniu poniższego programu?

public class Main {


public static void main(String[] args) {
new Main().metoda(30);
new Main().metoda(40, 50);
new Main().metoda(50, 60, 70);
}
void metoda(int i, Integer j) { System.out.print("int, Integer; ");}
void metoda(int ... liczby) { System.out.print("varargs; "); }
void metoda(long l, long k) { System.out.print("long, long; "); }
void metoda(short s) { System.out.print("short; "); }
void metoda(Integer i, Integer j, Long k) { System.out.print("Integer, Integer, Long; "); }
}

a) Program nie skompiluje się;


b) short; long, long; varargs;
c) varargs; long, long; Integer, Integer, Long;
d) varargs; long, long; varargs;
e) short; long, long; Integer, Integer, Long;
f) varargs; int, Integer; varargs;

3. Jaki będzie efekt wywołania poniższego programu?

public class Main {


public static void main(String[] args) {
new C(5);
}
}
class A {
public A() { System.out.print("4"); }
public A(int x) { this(); System.out.print("1"); }
}
class B extends A {
public B(int y) { this(); System.out.print("2"); }
public B() { super(2); System.out.print("5"); }
}

34 12/2009
Certyfikat SCJP

class Zewnetrzna { Wewnetrzna()) zapis. Nic nie stoi na prze- Zewnetrzna.Wewnetrzna w = new
class Wewnetrzna { szkodzie, aby powyższe dwie instrukcje Zewnetrzna.Wewnetrzna();
} sprowadzić do jednej:
} Różnicę tę można wytłumaczyć, wiążąc
Zewnetrzna.Wewnetrzna w = egzemplarze klas zagnieżdżonych z kla-
Najpierw musisz utworzyć obiekt klasy ze- new Zewnetrzna().new Wewnetrzna(); są, w której się znajdują. Podobieństwo do
wnętrznej: pól i metod statycznych jest aż nazbyt wi-
Inaczej ma się sytuacja w przypadku klas za- doczne.
Zewnetrzna z = new Zewnetrzna(); gnieżdżonych. Pod względem składni różni Na tym kończę pierwszą, najdłuższą część
je od klas wewnętrznych tylko jedno słowo cyklu. W kolejnych artykułach poruszę dal-
Następnie utwórz obiekt klasy wewnętrz- – static. Gdyby więc wprowadzić tę drob- sze zagadnienia; bardzo możliwe, że przy
nej, korzystając z otrzymanej przed chwi- ną zmianę: okazji będę uzupełniał też zagadnienia już
lą instancji: omówione. Tymczasem zachęcam do roz-
class Zewnetrzna { wiązania opracowanych przeze mnie za-
Zewnetrzna.Wewnetrzna w = z.new static class Wewnetrzna { dań, dotyczących całego zagadnienia 1. Po-
Wewnetrzna(); } wodzenia!
}
Konieczność korzystania z dwuczłonowej
nazwy klasy przy deklarowaniu zmien- to natychmiast przyniosłoby to zmiany
nej nie powinna dziwić. Z pewnością kon- w tworzeniu obiektów klasy Wewnetrzna :
strukcja obiekt.new Klasa() może z po-
czątku wydawać się tworem nienatural-
nym. Wynika to jednak z faktu, że egzem- KRZYSZTOF RYCHLICKI  KICIOR
plarze klas wewnętrznych są ściśle zwią- Autor programuje w Javie i .NET. Pisze książki i artykuły na różne tematy związane z programowaniem.
zane z konkretnymi egzemplarzami klas Jest posiadaczem certyfikatu SCJP; od kwietnia 2007 do kwietnia 2008 legitymował się tytułem Micro-
zewnętrznych. Z tego względu obowią- soft MVP w kategorii Visual C#.
zuje taki, a nie inny (np. Zewnetrzna.new Kontakt z autorem: kitikatpl@gmail.com

class C extends B {
public C(int z) { super(z); System.out.print("3"); }
public C() { this(2); System.out.print("6"); }
}

a) 41523;
b) 32514;
c) 35241;
d) 63542;
e) 41532.

4. Wskaż poprawne deklaracje i inicjalizacje zmiennych:

a) int $ _ zmienna = 3;
b) float liczba = 3.0;
c) int [] tablica[] = new int[][];
d) double _ 35 = 2.5;
e) char znak = 65;

5. Wybierz poprawne deklaracje elementów interfejsu:

a) int pole = 2;
b) public static int pole = 3;
c) public static int final pole = 4;
d) static final int pole = 5;
e) protected void metoda();
(jak w przypadku pól).
lub pusty modyfikator – nie oznacza on jednak dostępu w obrębie pakietu (tzw. default access), lecz niejawny modyfikator publiczny
opcji e zastosowano niedozwolony modyfikator protected (w przypadku interfejsów metody mogą zawierać modyfikator public
a, b, d – w opcji c modyfikator final występuje za typem int, co jest zabronione (typu i nazwy zmiennej nie można rozdzielać), w 5)
pamiętaj – wymiarów nie podaje się tylko, gdy w tej samej instrukcji następuje inicjalizacja!);
a, d, e – w opcji b brakuje litery f umieszczonej za literałem zmiennoprzecinkowym, w opcji c brakuje pierwszego lub obu wymiarów (za- 4)
a – spróbuj powoli przeanalizować kolejne wywołania, zapisując kolejno wypisywane cyfry – nie rób tego w pamięci; 3)
Long ;
patybilne wstecz; varargs, ponieważ Integer, Integer, Long w ogóle nie pasuje do trzeciego wywołania – z uwagi na parametr typu
d – varargs, ponieważ short w żadnym przypadku nie pasuje do literału typu int; long, long, ponieważ int, Integer nie jest kom- 2)
e – błąd w miejscu nr 1, a także w wierszu public enum Gatunki – w jednym pliku nie mogą występować dwie klasy publiczne; 1)

Odpowiedzi:

www.sdjournal.org 35
Programowanie Java

BlockingQueue w Javie
Prezentacja interfejsu BlockingQueue, kilku możliwych
jego wykorzystań, oraz słów kilka o programowaniu
wielowątkowym w Javie

Synchronizowana kolejka to podstawa wielu aplikacji wielowątkowych


– prawdopodobnie większość z nas kiedyś taką zaimplementowała.
Pytanie jednak brzmi: po co? Skoro często warto po prostu sięgnąć po
gotowe rozwiązanie, takie jak BlockingQueue.

żyć, że produkowanie obiektów jest szyb-


Dowiesz się: Powinieneś wiedzieć: sze niż ich konsumpcja, gdyż program dzia-
• Jak wykorzystywać BlockingQueue; • Podstawy języka Java; ła znacznie dłużej niż 1 sekunda. Wynika to
• Jak zaimplementować prosty schemat pro- • Problematykę programowania wielowątko- z dość czasochłonnej operacji wypisywania id
ducent-konsument. wego. na konsolę.
BlockingQueue oferuje, w prównaniu do
Queue, kilka nowych, użytecznych metod.
dziwne rzeczy, najprawdopodobniej doszło- Przyjrzyjmy się kilku z nich bliżej:
by do deadlocka lub rzucane byłyby wyjątki
Poziom NoSuchElementException, ArrayIndexOutO • offer(E e, long timeout, TimeUnit
trudności fBoundsException, NullPointerException timeUnit) – jeśli jest to możliwe, dodaje
czy inne (w zależności od implementacji ko- element e do kolejki, jeśli nie, czeka mak-
lekcji). symalnie timeout jednostek timeUnit

P
oczątkujący programiści często nie zdają Działoby się tak dlatego, iż operacje wyjmo- na zwolnienie się miejsca w kolejce. Jeśli
sobie w ogóle sprawy z problemów wie- wania i dodawania elementu nie są operacja- po upłynięciu tego czasu nie uda się do-
lowątkowości. Nie przeszkadzałoby im mi atomowymi. JVM, dopóki programista te- dać e, zwraca false. W przeciwnym wy-
to, gdyby nie fakt, że często ją wykorzystują, na- go nie sprecyzuje, nie daje żadnych gwarancji padku zwraca true.
wet o niej nie wiedząc. Doskonałym tego przy- co do momentu zmiany obsługiwanego wąt- • poll(long timeout, TimeUnit
kładem są aplikacje pisane w Swingu – jeśli bę- ku. W szczególności oznacza to, iż może to na- timeUnit) – pobiera pierwszy ele-
dziemy wykonywać skomplikowane operacje stąpić w połowie wyjmowania elemenu z ko- ment kolejki, jeśli kolejka jest pusta,
z wątku Swingowego, de facto wyłączymy na lekcji. Może to prowadzić do utraty spójności czeka maksymalnie timeout jedno-
ten czas aplikację, gdyż nie będzie ona przyjmo- kolekcji, a co za tym idzie do błędu. stek timeUnit na dodanie nowego ele-
wać od użytkownika żadnych poleceń. Ten problem synchronizacji rozwiązuje za mentu. Jeśli to nastąpi, zwraca go – je-
Java oferuje nam pewien zestaw klas, które nas Sun – nie jest to może rozwiązanie opty- śli nie, zwraca null.
bardzo ułatwiają programowanie wielowąt- malne, lecz na pewno skuteczne. W Listingu • put(E e) – dodaje do kolejki element e,
kowe. W szczególności są to bezpieczne (thre- 1 przedstawiona jest prosta klasa demonstru- czekając, jeśli to konieczne, na wolne w
ad-safe) kolekcje, jak na przykład te imple- jąca mechanizm producenta i konsumenta. niej miejsce. Warto zauważyć, że meto-
mentujące opisywany interfejs BlockingQu- Przedstawiona w Listingu 1 klasa tworzy syn- da ta nie ma maksymalnego czasu cze-
eue. Polecam zapoznanie się z zawartością pa- chronizowanąkolejkę–LinkedBlockingQueue, kania, może więc czekać, de facto, w nie-
kietu java.util.concurrent, do którego odno- następnie tworzy 10 producentów, którzy do skończoność.
śnik znajdziecie w artykule w częsci W Sieci. tej kolejki będą dodawać instancje klasy Item, • take() – pobiera pierwszy element ko-
oraz 10 konsumentów, którzy będą je z niej po- lejki, jeśli to konieczne, czekając na je-
Problem bierać. Ponadto, każdy obiekt klasy Item ma go pojawienie się. Podobnie jak w przy-
producenta i konsumenta przypisane id, które będzie wyświetlane po je- padku put(E e), może czekać w nie-
Jest klasycznym problemem synchronizacji. go pobraniu. Warto przyjrzeć się kolejności wy- skończoność. Z tą metodą należy uwa-
Ma on miejsce wtedy, kiedy w aplikacji ma- świetlanych id – wcale nie będzie (a raczej: naj- żać, gdyż np. w sytuacji, gdy program
my dwa rodzaje obiektów, z których jeden prawdopodobniej nie będzie) ona kolejnością kończy pracę, kolejka, z której pobie-
dodaje do wspólnej puli produkowane jed- jednostajnie rosnącą. raliśmy elementy, jest pusta i nie będą
nostki, zaś drugi je pobiera. Gdybyśmy jako Utworzeni producenci pracują przez ok. do niej dodawane nowe elementy, po
wspólną pulę wykorzystali zwykłą listę (np. jedną sekundę, zaś konsumenci aż do mo- wywołaniu tej metody program nigdy
LinkedList, ArrayList) zaczęłyby się dziać mentu opróżnienia kolejki. Można zauwa- nie zakończy działania. Chyba że dany

36 12/2009
BlockingQueue w Javie

wątek będzie oznaczony jako Daemon Wykonywanie poleceń użytkow- Klasa ta realizuje wzorzec singletonu, co
Thread . nika: prosty CommandThread oznacza, że w całej aplikacji istnieje dokładnie
Już na początku artykułu pisałem o problemie jedna instancja klasy Commander. Będzie ona
Na koniec tej części pytanie: czy pewne jest, wykonywania poleceń w innym wątku, niż by- tworzona statycznie w momencie startu apli-
że id obiektów Item będą unikalne? A może ły one wydane. Na Listingu 2 znajduje się prosta kacji, wówczas też będzie uruchamiany wątek,
możliwe jest, żeby któryś z nich się powtarzał klasa, która może do tego posłużyć, oraz związany z którego wykonywane będą polecenia.
lub został pominięty? Podpowiedź: czy opera- z nią interfejs polecenia. Oczywiście interfejs i kla- Do wykorzystania tego obiektu niezbędna jest
cja id = idCount++ jest operacją atomową? sa powinny znaleźć się w oddzielnych plikach. tylko jedna metoda – submitCommand(Command

Listing 1. Przykład prostego rozwiązania problemu producenta i konsumenta

package org.wawer.sdj.blockingQueueArt;
try {
import java.util.concurrent.BlockingQueue; System.out.println(queue.poll(10,
import java.util.concurrent.LinkedBlockingQueue; TimeUnit.MILLISECONDS));
import java.util.concurrent.TimeUnit; } catch (InterruptedException e) {
System.out.println("Interrupted while
/** polling");
* Klasa demonstrująca działanie BlockingQueue }
* }
* @author Dariusz 'Scythe' Wawer }
*/ }
public class SimpleBlockingQueueExample {
volatile boolean stop = false;
/**
* Klasa, której instancje będą przekazywane do i BlockingQueue<Item> queue;
pobierane z kolejki
*/ public SimpleBlockingQueueExample() {
static class Item { // utworzenie kolejki
static int idCount; queue = new LinkedBlockingQueue<Item>();
int id; }
public Item() {
id=idCount++; public void buildProducers() {
} for (int n=0; n<10; n++) {
new Thread(new Producer()).start();
public int getId() { }
return id; }
}
public void buildConsumers() {
@Override for (int n=0; n<10; n++) {
public String toString() { new Thread(new Consumer()).start();
return Integer.toString(id); }
} }
}
public static void main(String[] args) {
class Producer implements Runnable{
public void run() { SimpleBlockingQueueExample ex = new SimpleBlockingQue
while (!stop) { ueExample();
try { ex.buildProducers(); // przygotujmy producentów...
queue.put(new Item()); ex.buildConsumers(); // ... oraz konsumentów
} catch (InterruptedException e) { try {
System.out.println("Interrupted while // pozwólmy przez jakiś czas działać pozostałym
putting"); wątkom
} Thread.sleep(1000);
} } catch (InterruptedException e) {
} System.out.println("Main thread interrupted");
} }
ex.stop = true; // zakańczamy działanie producentów
class Consumer implements Runnable{
public void run() { }
while (!stop || queue.size()>0) { }

www.sdjournal.org 37
Programowanie Java

command). Do kolejki zdarzeń do wykonania do- Zastanówmy się teraz, skąd wzięła się kon- głoby ono nie zostać zakończone, gdyż we
pisywane jest wówczas polecenie podane w ar- strukcja peek-execute-poll. A w tym celu flushAndStop() sprawdzana jest ilość pole-
gumencie. przyjrzyjmy się metodzie flushAndStop(). ceń w kolejce, a nie, czy zostały one wykona-
Odpowiem na niezadane pytanie: tak, wyko- Po jej wywołaniu nastąpi zatrzymanie wątku, ne do końca.
rzystywana jest implementacja BlockingQueue. z którego zostanie wywołana, do czasu opróż- Tak jak pisałem, implementacja ta jest do-
Można by mieć wątpliwości, czy jest to koniecz- nienia kolejki. Po co nam ona, skoro wątek jest syć prosta. Można ją rozszerzyć, np. używając
ne – w końcu polecenia wydawane są z wątku demonem, czyli wyłączy się, jeśli wyłączą się PriorityBlockingQueue oraz odpowiednie-
GUI, zaś pobierane są też tylko w jednym wąt- wszystkie wątki nie będące demonami? Oczy- go komparatora. Można, w ramach optyma-
ku. Niestety, nawet te dwie pozornie oddzielne wiście jej celem jest upewnienie się, że wszyst- lizacji dla dużej ilości małych poleceń, przy
operacje mogą wywołać błędy – niesynchroni- kie polecenia znajdujące się aktualnie w kolej- każdej iteracji dodawać do lokalnej, niesyn-
zowana kolejka ma szansę nie poradzić sobie w ce zostaną wykonane do końca. Można tego chronizowanej kolejki wszystkie polecenia z
momencie, gdy jednocześnie nastąpi próba po- używać np. gdy chcemy, by nasza aplikacja au- tej synchronizowanej (BlockingQueue.dra-
brania i dodania elementu do kolejki. Ponadto, tomatycznie zapisywała jakiś plik w momen- inTo(Collection coll)).
wcale nie jest pewne, że polecenia dodawane bę- cie jej zamknięcia. Wówczas na koniec dodaje-
dą tylko z jednego wątku. Nie jest powiedziane, my do kolejki odpowiednie polecenie, po czym Inne mechanizmy
że jedno z poleceń może zlecić wykonanie inne- wywołujemy flushAndStop(). programowania współbieżnego
go. A zlecenie to będzie następowało już z wąt- Gdyby wątek wykonujący polecenia od W pakiecie java.util.concurrent znajduje
ku Commandera, a nie z GUI. razu pobierał polecenie do wykonania, mo- się wiele klas pomocnych w programowa-

Listing 2. Prosta klasa asynchronicznie wykonująca polecenia

package org.wawer.sdj.blockingQueueArt; @Override


public void run() {
import java.util.concurrent.LinkedBlockingQueue; Command command = null;
import java.util.concurrent.TimeUnit; while(keepRunning) {
try {
public class Commander { command = commandQueue.peek(100,
TimeUnit.MILLISECONDS);
class CommandExecuter implements Runnable { } catch (InterruptedException e1) {
volatile boolean keepRunning = true; System.out.println("Event writer thread
interrupted while waiting");
BlockingQueue<Command> commandQueue; }
if (command!=null) {
public CommandExecuter() { executeCommand(command);
commandQueue = new LinkedBlockingQueue<Command>(); commandQueue.poll();
}
Thread t = new Thread(this, "Command Executing }
Thread"); }
t.setDaemon(true); }
t.start();
} private static Commander instance = new Commander();

private void executeCommand(Command command) { CommandExecuter commandExecuter;


command.execute();
} private Commander() {
commandExecuter = new CommandExecuter();
public void flushAndStop() { }
while (commandQueue.size()>0) {
try { public static final void submitCommand(Command command) {
Thread.sleep(20); instance.commandExecuter.enqueueCommand(command);
} catch (InterruptedException e) { }
System.out.println("Commander interrupted
while sleeping"); public static void flushAndStop() {
} instance.commandExecuter.flushAndStop();
} }
keepRunning = false; }
}
public interface Command {
public void enqueueCommand(Command command) {
commandQueue.add(command); public void execute();
} }

38 12/2009
BlockingQueue w Javie

niu wielowątkowym. Pokrótce opiszę kil- jeśli już ich nie posiada, wstrzymuje ich dzo kosztowna z punktu widzenia prędko-
ka z nich: działanie do uzyskania nowych. ści działania.
• Lock – pozwala na zarządzanie dostę- Nie warto także przesadzać i stosować syn-
• CountDownLatch,
CyclicBarrier – pem do współdzielonego zasobu. Istnieje chronizowanych kolekcji wszędzie, nawet je-
klasy ułatwiające synchronizację dzia- kilka implementacji tego interfejsu, ich śli nie są one konieczne. Często jeden prosty
łań kilku wątków. Pozwalają monito- dobór powinien być uzależniony od ro- blok synchronized(lock) {...} jest w sta-
rować postęp pracy w każdym z nich dzaju zarządzanego zasobu. nie rozwiązać problem. Z drugiej strony, czę-
oraz wznawiać pracę głównego wątku sto zdarzają się sytuacje, w których od efek-
po osiągnięciu postawionej przed nimi Zachęcam do szczegółowej analizy każdej tywności ważniejsza jest prostota.
pracy. z tych klas. Ich znajomość może oszczę- Innymi słowy, programista musi podjąć de-
• Executor, ExecutorService – inter- dzić sporo czasu podczas tworzenia apli- cyzję: czy wykorzystać, potencjalnie powol-
fejsy do wykonywania zadań. Posia- kacji. ne, istniejące rozwiązanie, czy zaimplemen-
dają własny wątek lub pulę wątków, tować coś szybszego samemu, kosztem wła-
w których wykonywane są polecenia. Podsumowanie snego czasu. Warto też pamiętać, że z każdą
Szczególnie przydatne np. w aplika- Programowanie wielowątkowe jest, nieste- wersją Javy poszczególne implementacje sta-
cjach serwerowych, gdzie konieczne ty, dość skomplikowane. Można jednak sto- ją się coraz bardziej efektywne. Więc jeśli ma-
jest obsłużenie wielu wywołań jedno- sunkowo łatwo ominąć część jego proble- my wątpliwości, co zrobić, prawdopodobnie
cześnie. mów, wykorzystując już gotowe klasy napi- lepiej wykorzystać już istniejącą klasę.
• Semaphore – zarządza pewną pulą zaso- sane przez innych. Trzeba jednak pamiętać, Jest ku temu jeszcze jeden powód: zasa-
bów, czy to fizycznych czy logicznych. że nierzadko będzie się to działo kosztem dy optymalizacji. Link do nich znajduje się
Wydaje je poszczególnym wątkom, zaś optymalności, gdyż synchronizacja jest bar- w części W Sieci – zapoznanie się z nimi po-
zwoliło mi oszczędzić naprawdę dużo cza-
su i pracy.
W Sieci
DARIUSZ 'SCYTHE' WAWER
• http://java.sun.com/javase/6/docs/api/java/util/concurrent/BlockingQueue.html; Student 4 roku Telekomunikacji na Politechnice
• http://java.sun.com/javase/6/docs/api/java/util/concurrent/package-summary.html;
• http://pl.wikipedia.org/wiki/Problem_producenta_i_konsumenta; Warszawskiej, Java Developer w firmie CC Otwar-
• http://www.c2.com/cgi/wiki?RulesOfOptimization. te Systemy Komputerowe
kontakt: dariusz.wawer@cc.com.pl

R E K L A M A

www.sdjournal.org 39
Języki programowania

Delphi
i C++Builder 2010
Nowości wprowadzone w Delphi© 2010 i C++Builder© 2010

Środowisko Delphi powstało w roku 1995, C++Builder dwa lata później.


Mimo upływu czasu ich architektura (komponenty VCL, programowanie
wizualne) nadal zapewnia przewagę nad innymi platformami
programistycznymi, w obszarze budowy aplikacji okienkowych dla
systemu Windows.
Już od kilku edycji środowisko Galileo (ro-
Dowiesz się: Powinieneś wiedzieć: bocza nazwa środowiska bazowego dla Delphi
• Jakie nowości wprowadzono do środowisk • Czytelnik powinien być programistą Delphi i C++Buildera) pozwala łączyć w sobie kilka
Delphi i C++Builder; lub C++Buildera. kompilatorów, debuggerów, narzędzi i kreato-
• Czym jest pakiet Embarcadero RAD Studio. rów dla wielu języków. Tak też jest tym razem.
W pakiecie RAD Studio elementy Delphi
i C++Builder są połączone w jednym wspól-
VCL, wizualne komponenty oraz otwarty kod nym środowisku.
źródłowy większości bibliotek. Delphi nadal Delphi Prism 2010 pozwala budować apli-
Poziom jest jednym z najbardziej znanych i najczęściej kacje oparte o kod zarządzalny i wirtualną ma-
trudności używanych środowisk programistycznych. Sza- szynę .NET, w pełni wykorzystuje możliwości
cuje się, iż grupa programistów, którzy korzysta- biblioteki programistycznej FCL oraz biblio-
ją ze środowisk Delphi / C++Builder, liczy obec- tek wyższego poziomu, takich jak WPF. Wspie-
nie ponad 1,7 mln (wliczając w to użytkowni- ra tworzenie aplikacji okienkowych Windows

N
owe Delphi i C++Builder zawie- ków wersji darmowych). Forms i webowych ASP.NET w języku Delphi.
rają tak dużą liczbę ulepszeń oraz Ogromnym atutem omawianych narzędzi Delphi Prism w porównaniu do Visual Studio
nowych funkcji, że poniższy arty- jest kompilowanie kodu do postaci natywnej oferuje łatwy i przejrzysty język programowania
kuł dotyczy tylko cześć z nich. Reszta została dla systemu Windows, czyli korzystając z plat- (Delphi) oraz możliwość tworzenia kodu nie
zamieszczona w wersji on-line (adres WWW formy Win32 API. Nowa wersja Delphi (rów- tylko dla platformy .NET, ale również dla Mo-
na końcu tego artykułu). Omawia ona mię- nież C++Buildera) oraz kolejne planowane edy- no – wieloplatformowej odmiany .NET działa-
dzy innymi niezwykle innowacyjne wsparcie cje środowisk mają nadal być oparte o kod na- jącej na systemach: Windows, Linux i MacOS.
dla aplikacji opartych o gesty i naturalny in- tywny, zarówno dla Win32, jak również w przy- Platforma Mono ma dużo większe wsparcie w
terfejs, a także nowości bazodanowe. szłości dla Win64. Aplikacje natywne napisa- Delphi Prism, niż to, co jest oferowane w narzę-
Delphi i C++Builder to środowiska progra- ne w Delphi/C++Builder mają pełny dostęp do dziach firmy Microsoft, która w swoich narzę-
mistyczne typu RAD rozwijane obecnie przez możliwości sprzętowych i systemowych Win- dziach wspiera jedynie swój system operacyjny.
firmę Embarcadero Technologies, wcześniej dows. Nie potrzebują one dodatkowej warstwy,
Borland. Delphi powstało na bazie doświad- jaką jest wirtualna maszyna (VM). Nowości w IDE
czeń z obiektowej wersji Turbo Pascala. Cieka- Od kilku wersji w Delphi mocno rozwijane
wy jest fakt, że przyjęte 14 lat temu założenia Wersje 2010 jest IDE (zintegrowane środowisko programi-
nie straciły swojej wartości. Architektura całej W sierpniu 2009 firma Embarcadero zapo-
biblioteki VCL, w tym koncepcja komponen- wiedziała wypuszczenie nowej edycji flago-
tów VCL, których funkcjonalność można ła- wych produktów dla programistów. Tym ra-
two rozszerzać, oraz idea programowania wizu- zem postanowiono równocześnie wprowa-
alnego, nadal pozwala błyskawicznie tworzyć dzić wszystkie środowiska przeznaczone dla
aplikacje okienkowe, według wielu programi- programistów systemu Windows. Na rynku
stów szybciej niż w innych narzędziach. pojawiły się: Delphi 2010, C++Builder 2010
Mocne strony Delphi i C++Buildera to kom- i Delphi Prism 2010 oraz pakiet Embarcade-
Rysunek 1. Porównanie aplikacji natywnej do
pilacja kodu do postaci natywnej systemu Win- ro® RAD Studio, łączący trzy pierwsze śro- aplikacji zarządzalnych opartych o wirtualną
dows, obiektowa biblioteka programistyczna dowiska w jeden zintegrowany pakiet. maszynę (VM)

40 12/2009
Delphi 2010 / C++Builder 2010

sty), czyli edytor kodu, projektant formatek, w oknie wyników). Nie trzeba przeglądać kla- stowaniu takich aplikacji poważnym kłopo-
debugger, menedżer projektu oraz inne na- sycznego menu, a wykorzystanie myszki przy tem było identyfikowanie wątków przez liczbę,
rzędzia i kreatory wspomagające tworzenie kodowaniu można ograniczyć do minimum. a nie przez nazwę. Mimo tego, że istniała moż-
aplikacji. W efekcie z wersji na wersję wzra- Zaawansowani programiści też powinni polu- liwość zmiany tej nazwy już wcześniej, to była
sta szybkość kodowania i komfort pracy. Wie- bić funkcję IDE Insight, dzięki której będą mo- ona skomplikowana. Teraz dodano property do
le z wprowadzonych nowości dostępnych jest gli wykonywać znacznie szybciej wiele operacji klasy TThread pozwalające w bardzo prosty spo-
dla obu języków Delphi i C++, ale są też takie, na kodzie aplikacji, ograniczając użycie myszki sób nazwać nowe wątki. TThread jest klasą bazą
które są przeznaczone dla jednego z nich. do minimum. dla każdego wątku napisanego w Delphi i C++.
Debugger wykorzystuje te nazwy, wyświetlając
IDE Insight Formater kodu czytelną nazwę zamiast liczbowego identyfika-
Nowością wprowadzoną do IDE w środowi- Dostępny zarówno dla języka Delhi, jak i C++ tora, co ogromnie ułatwia rozpoznanie wątku,
sku Delphi/C++Builder 2010 jest funkcja pozwala automatycznie formatować kod, za- a co za tym idzie precyzyjną diagnozę błędów.
IDE Insight. Patrząc na pierwsze komenta- pewniając jednolity wygląd kodu źródłowe- Nazwa i stan wszystkich stworzonych wątków
rze programistów na blogach, można stwier- go niezależnie od stylu pisania osoby, która go widoczny jest po otwarciu okna Thread Status.
dzić, że została ona przyjęta bardzo entuzja- stworzyła lub poprawiała. Ponadto w nowych środowiskach możli-
stycznie. Pozwala wykonać wszystkie opera- Formater uruchamia się jednym skrótem we jest zamrożenie i aktywowanie dowolnego
cje z jednego okna, w którym można odna- klawiszowym i w chwilę później kod w ak- z wątków, ustawienie pułapki (ang. breakpoint)
leźć kontekstowo każdą funkcję, każdy kom- tywnym oknie zostaje ułożony zgodnie ze na konkretnym wątku oraz bieżące śledzenie
ponent i każdą opcję środowiska. zdefiniowanymi zasadami. Zasady te to ogól- stanu i wartości zmiennych w wybranym wąt-
Aby zrozumieć zalety takiego rozwiązania, ne, powszechnie stosowane reguły dla danego ku. Dzięki tym zmianom poszukiwanie błędów
trzeba najpierw porównać styl pracy początkują- języka oraz kilkadziesiąt opcji, które pozwa- w wielowątkowych aplikacjach jest dużo szybsze,
cego i zaawansowanego programisty. Pierwszy z lają dopasować ostateczny wygląd do indywi- bardziej precyzyjne i o wiele przyjemniejsze.
nich wpisuje kod z klawiatury, nie korzystając z dualnych upodobań. Dostępne opcje dotyczą
automatycznego uzupełniania kodu, nie używa sposobu wcinania kodu, reguł wstawiania od- Eksplorator klas C++
funkcji refaktoringu, nie zna wielu kreatorów, stępów, przechodzenia do nowej linii oraz Kolejna nowość jest potwierdzeniem otwarcia
bardzo często korzysta na przemian z myszki i z użycia dużych lub małych liter w nazwach nowego właściciela opisywanych narzędzi, czy-
klawiatury, nie zna wielu użytecznych opcji ani i w słowach kluczowych. li firmy Embarcadero, na potrzeby klientów.
skrótów klawiszowych, a w efekcie pracuje du- Formater doskonale sprawdzi się w mo- Wśród użytkowników środowiska C++Buil-
żo wolniej. Dzięki IDE Insight wszystkie ukryte mencie modyfikowania obcego kodu, np. der najbardziej oczekiwanym rozszerzeniem
funkcje są dostępne po naciśnięciu jednego kla- odziedziczonego po poprzednim autorze.
wisza [F6] lub kombinacji [Ctrl+.]. Poza ogromną wygodą przy doprowadzaniu
W oknie IDE Insight kluczowe jest pole wy- obcego kodu do swoich standardów funkcja
szukiwania kontekstowego (u góry okna), które ta może być przydatna w pracy zespołowej.
pozwala błyskawicznie odnaleźć funkcję wśród Dzięki niej kod składowany w centralnym re-
tysięcy dostępnych. Początkowo odnalezienie pozytorium będzie sformatowany tak samo,
szukanego polecenia może zajmować więcej bez względu na to, kto go pisał.
czasu, a osoba nieznająca nazwy szukanej funk-
cji będzie musiała próbować kilka razy. Jednak Debuggowanie wątków
bardzo szybko można się tego nauczyć (meto- Delphi 2010 i C++Builder 2010 wprowadza-
dą prób i błędów), a przy okazji poznać skróty ją kilka zmian, które mają na celu ułatwienie
klawiszowe związane z funkcjami (wyświetlane testowania aplikacji wielowątkowych. Przy te-

Rysunek 3. Okno IDE Insight i wynik


kontekstowego wyszukiwania słowa Project

Rysunek 4. Kod źródłowy Delphi po


uruchomieniu funkcji formatowania

Rysunek 2. Ekran startowy RAD Studio 2010 – połączone w jednym środowisku funkcje Delphi i Rysunek 5. Okno „Thread Status” i widoczne w
C++Buildera nim nazwy oraz stan wątków

www.sdjournal.org 41
Języki programowania

był Class Explorer, czyli okno ułatwiające za- uważyło ich obecności. Aktualnie audyty i me- jektu. Bardzo ciekawa jest funkcja prezentacji
rządzanie większymi strukturami obiektowy- tryki są samodzielnymi narzędziami, co zdecy- wyników w formie grafu Kivat, dostępnego dla
mi. W serwisie Quality Central zdobył on naj- dowanie zwiększa ich atrakcyjność i powinno kodu napisanego w języku Delphi.
większą liczbę głosów. wpłynąć na ich popularność. Diagram Kivat to wykres kołowy, gdzie po-
Nowy eksplorator klas pozwala na nawi- Metryka kodu to miara określająca jego szczególne metryki ułożone są promieniście
gację po dziesiątkach, setkach, a nawet tysią- stan. Najprostsze i najpopularniejsze metry- na liniach rozchodzących się ze środka diagra-
cach klas projektu. Dzięki różnym sposobom ki to: liczba linii kodu (LOC – Lines Of Code) mu. Czerwony okrąg wskazuje granicę warto-
grupowania, takim jak grupowanie moduła- lub liczba klas (NOC – Number Of Classes). ści dopuszczalnej dla metryki. Punkt na pro-
mi lub hierarchiczne, można błyskawicznie W sumie w Delphi 2010 mamy dostępnych mieniu wskazuje wynik metryki dopasowany
odnaleźć potrzebną klasę. W prosty sposób ponad 100 metryk wyliczanych automatycz- proporcjonalnie do wartości maksymalnej.
można ją rozszerzyć za pomocą wbudowa- nie, dotyczą one spójności kodu (ang. cohe- Jeżeli wartość danej metryki przekracza do-
nych funkcji do wizualnego dodawania pól, sion), jego złożoności (ang. complexity), wza- puszczalny zakres, to punkt wychodzi poza
własności (properties) oraz metod. jemnego sprzężenia klas (ang. coupling), dzie- okrąg, a jeżeli jest w porządku, to znajduje się
Ponadto możliwe jest sprawdzenie miejsc dziczenia, polimorfizmu i innych obszarów. on we wnętrzu okręgu. Patrząc na taki graf,
użycia wybranej klasy w całym projekcie oraz Przykładem zaawansowanej i złożonej me- można niemal natychmiast ocenić najwięk-
sprawdzenie diagramu hierarchii klas. tryki może być cyklometryczna zależność (CC sze mankamenty stworzonego kodu.
Nowy Class Explorer bardzo ułatwia pracę – Cyclomatic Complexity) – wprowadzona przez Diagram tego typu poza wartością praktycz-
programistom opierającym swój kod o klasycz- Thomasa McCabea w roku 1976. Jest ona bar- ną ma również dodatkowe zalety, na przykład
ne programowanie obiektowe, wspiera również dzo popularna w działach jakości dużych firm może być wyznacznikiem jakości projektu
tych programistów RAD, którzy wykorzystują programistycznych – pozwala wyliczyć bowiem oraz stanowić część dokumentacji technicznej
w swojej pracy komponenty i zdarzenia. liczbę wszystkich wariatów różnych ścieżek ja- wysyłanej w wewnętrznych raportach dla kie-
kie prowadzą przez kod. Metryka ta pozwala rowników. Co więcej, metryki uruchamiane
Metryki kodu Delphi także oszacować nakłady potrzebne na testowa- systematycznie co pewien okres czasu, np. raz
Z kolei programiści Delphi dostają w wersji nie kodu; im większą wartość otrzymamy, tym w tygodniu, mogą wskazywać postęp projektu
2010 dwa w pełni samodzielne narzędzia ana- bardziej kosztowne i trudniejsze będzie dokład- oraz regresję lub poprawę jego jakości.
lityczne ułatwiające utrzymanie jakości kodu ne testowanie. Ta metryka, podobnie jak wiele
na wysokim poziomie. Poprawiono integrację innych, dostępnych teraz dla programistów Del- Audyty kodu Delphi
ze środowiskiem narzędzi do automatycznego phi, pozwala otrzymać bardzo cenne podpowie- Drugim narzędziem analitycznym jest auto-
przeglądu kodu (tzw. audytów) oraz do przeli- dzi, jak poprawić swój projekt lub gdzie możemy mat wykonujący audyty kodu. Audyt polega
czania metryk kodu Delphi. Choć zostały one spodziewać się największych problemów. na sprawdzeniu kodu źródłowego pod wzglę-
wprowadzone do IDE już wcześniej, wymaga- Dla każdej metryki określona jest wartość dem miejsc wrażliwych. W wyniku działania
ły włączenia funkcji do modelowania UML. minimalna lub maksymalna, którą dodatkowo tej funkcji wskazywane są miejsca, które są napi-
Dlatego wielu użytkowników nawet nie za- można dostosowywać do specyfiki swojego pro- sane w sposób mało optymalny oraz takie, któ-
re choć mają poprawną składnię, to są potencjal-
nie niebezpieczne, czyli mogą być źródłem po-
ważnych błędów. W Delphi 2010 dostępne jest
około setki automatycznych audytów.
Początkowo audyty mogą wydawać się być
pewną odmianą ostrzeżeń kompilatora (war-
nings) i faktycznie mają cechę wspólną, tzn.
zarówno jedne, jak i drugie dają podpowie-
dzi, jak poprawić kod źródłowy. Poza tym
różnią się zasadniczo. Audyty dokonują ana-
lizy rekurencyjnie porównując ze sobą bloki
kodu. Takie działanie, choć jest dużo bardziej
czasochłonne, to pozwala o wiele precyzyjniej
zdiagnozować kod aplikacji.
Wykorzystanie audytów może pomóc
głównie programistom, ale również kierow-
nicy mogą z nich korzystać przy analizie ja-
kość kodu członków zespołu lub jako źródło
dobrych praktyk programowania.

Pozostałe nowości IDE


Poza wymienionymi wcześniej nowe narzę-
Rysunek 6. Nawigator klas dla programistów C++ dzia Delphi / C++Builder 2010 oferują wiele
innych usprawnień zintegrowanego środowi-
ska programistycznego. Warto pokrótce wy-
Serwis Quality Central mienić ważniejsze z nich.
Quality Central to serwis Web dostępny dla wszystkich zarejestrowanych użytkowników środowisk
(darmowych i komercyjnych) firmy Embarcadero. Służy do zgłaszania wykrytych błędów oraz po-
mysłów dla nowych funkcji. Na zgłoszone przez innych pomysły można oddać swój głos, zwiększa- • Wizualizatory (ang. Visualizers) to roz-
jąc w ten sposób jego priorytet. Adres serwisu Quality Central: http://qc.embarcadero.com szerzenia IDE, które pozwalają w czytel-
ny sposób zaprezentować zakodowane i

42 12/2009
Delphi 2010 / C++Builder 2010

złożone typy danych w czasie testowania lue replacer), a drugi prezentuje listę napi- • Rozbudowane Open API – pozwala na
(debugowania) programu. Do środowi- sów w dodatkowym okienku (wizualiza- rozszerzanie funkcji środowiska o wła-
ska 2010 dołączone są dwa przykładowe tor typu external viewer). sne funkcje i kreatory, które modyfiku-
wizualizatory: dla typu TDateTime oraz ją i rozbudowują kod źródłowy, dodają
dla listy napisów TStringList. Pierwszy Do programów przykładowych dołączone są komponenty, ustawiają properties i wyko-
z nich zamienia zakodowaną datę i czas kompletne źródła obydwu wizualizatorów, a nują wiele innych zadań. Nowe rozszerze-
na czytelny napis (wizualizator typu va- na ich bazie można stworzyć swoje własne. nia pozwalają na korzystanie z niedostęp-
nych dotychczas obszarów IDE. Ponad-
to wciąż uzupełniana jest dokumentacja
otwartego API środowiska.
• Component Toolbar – dla wszystkich
programistów, którzy bardzo polubi-
li stary pasek komponentów, dostępny
w Delphi 7, C++Builder 6 i starszych
środowiskach, wprowadzono nową pa-
letę komponentów, wyglądającą niemal
identycznie jak ta z Delphi 7.

Dostępna jest też bardzo wygodna nowa pa-


leta (Tool Palette) wprowadzona w nowych
środowiskach. Oba okna mogą być otwarte
równocześnie i używane zamiennie.

• Kompilacja w tle – ciekawą opcją, szczegól-


nie dla programistów C++, jest możliwość
uruchomienia kompilacji i budowania ko-
du w tle. Przed rozpoczęciem takiej kom-
pilacji aktualna postać kodu jest kopiowana
w pamięci i zapamiętywana. W czasie całej
kompilacji programista może przeglądać,
a nawet modyfikować kod bez wpływu na
wynik kompilacji. Już nawet średnie pro-
Rysunek 7. Diagram Kivat Graph prezentujący metryki, które przekraczają wartości dopuszczalne jekty C++ potrafią budować się kilka mi-
nut i nowa funkcja pozwala programiście
pracować nawet w trakcie kompilacji.

Nowości w języku Delphi


Delphi 2010 jest pierwszą od wielu lat wersją,
w której wprowadzono istotne zmiany na pozio-
mie języka Delphi. Nowości te nie zmuszają do
zmiany dotychczasowych przyzwyczajeń progra-
mistów, ale dają im nowe możliwości. Dwie nie-
zwykle ważne zmiany w tym obszarze to: nowy
silnik RTTI oraz możliwość znakowania klas.
Pomimo iż w Delphi mechanizm RTTI jest
dostępny od wielu lat, był jednak tak skompli-
kowany, że tylko najbardziej zaawansowani
programiści mogli poznać jego ogromne zale-

Rysunek 8. Okno wyboru automatycznych audytów kodu oraz podpowiedź dla zaznaczonego Rysunek 9. Wizualizator typu external viewer
audytu DC – Duplicated Code prezentujący listę napisów (TStrings)

www.sdjournal.org 43
Języki programowania

ty. Był mało popularny, mimo tego, że jego uży- Nowy RTTI pozwala pobierać informacje Samo dodanie znaczników do klasy zapisu-
cie pozwalało znacznie skrócić kod oraz two- o typach obiektowych, typach prostych, wyli- je jedynie meta informacje, które można od-
rzyć rozwiązania o wiele bardziej elastyczne. czeniowych i innych. Dodatkowo można wy- czytać przez RttiContext. Kluczowy jest kod,
Czasami sprawiał on problemy nawet dla pro- woływać metody w obiektach, ustawiać i od- który odczyta oraz wykorzysta te atrybuty i na
gramistów, którzy go poznali, ponieważ brako- czytywać property oraz odczytywać nazwy i ich podstawie zrealizuje oczekiwane zadania.
wało w nim niektórych możliwości. parametry znaczników (opisane poniżej). Znaczniki powinny przetwarzać wyspecjalizo-
Teraz w Delphi 2010 wprowadzono zupeł- Drugą równie ważną, a być może nawet waż- wane biblioteki, dla których atrybut jest podpo-
nie nowy, obiektowy i prosty w użyciu mecha- niejszą innowacją wprowadzoną do języka Del- wiedzią, co należy zrobić z obiektem tej klasy,
nizm RTTI. Równocześnie ma dużo większe phi jest opcja znakowania klas. Są one oznacza- na przykład jak go utrwalić, które pola spraw-
możliwości niż swój poprzednik. Aktualnie ne przez dodanie atrybutów (ang. custom attri- dzić przy walidacji itd..
wszystkie informacje o typach odczytuje się z butes). Za pomocą znaczników możliwe jest Znaczniki dają ogromne pole do działa-
jednego obiektu, jakim jest RttiContext, wła- rozszerzanie funkcjonalności klasy o dodatko- nia dla autorów bibliotek i wyspecjalizowa-
śnie jego metody i własności (properties) po- we elementy, takie jak: utrwalanie w bazie da- nych platform programistycznych. Pozwala-
zwalają poznać informacje niemal o wszyst- nych, logowanie, walidacja wartości, wystawia- ją one w czytelny i prosty sposób oddzielać lo-
kich typach, jakie są używane w aplikacji. nie jako usługa sieciowa i wiele innych. giczne warstwy aplikacji, ułatwiając utrzyma-
nie modelu biznesowego odseparowanego od
mechanizmów przetwarzania. Znaczniki po-
jawiły się w Delphi po raz pierwszy, dlatego
w tej chwili jeszcze trudno oceniać ich wpływ na
technikę programowania, ale patrząc na języki,
Rysunek 10. Paleta komponentów jak w Delphi 7
w których są one od jakiegoś czasu (np. w Javie),
można stwierdzić, że ich wpływ na sposób pra-
Listing 1. Przykładowe użycie kontekstu RTTI cy programistów powinien być znaczny.
rttiType := rttiContext.GetType(TFoo);
for rttiField in rttiType.GetFields do Nowości w VCL
...; oraz dla baz danych
for rttiMethod in rttiType.GetMethods do W najnowszych środowiskach Delphi i C++Bu-
...; ilder wprowadzono tak wiele istotnych zmian,
for rttiProperty in rttiType.GetProperties do że opisanie ich wszystkich w ramach jednego
...; artykułu spowodowałoby, iż stałby się zbyt ob-
szerny. Osoby zainteresowane poznaniem no-
Listing 2. Oznakowana klasa TContact wej funkcjonalności narzędzi zapraszamy do
type przeczytania drugiej części artykułu (WWW).
[DBTable('CUSTOMERS')] W artykule zostaną opisane nowości związane
TContact = class ze zmianami w bibliotece i komponentach VCL,
private w tym innowacyjna możliwość tworzenia apli-
[DBField('COMPANY_NAME')] kacji dotykowych opartych o gesty i tak zwany
FCustomer: string; interfejs naturalny. Zaprezentowane zostaną tak-
[DBField('CONTACT_FIRST')] że nowości dotyczące wsparcia dla systemu Win-
FFirstName: string; dows 7 oraz wsparcia dla obsługi relacyjnych baz
[DBField('CONTACT_LAST')] danych, w tym programowanie dla serwera Fi-
FLastName: string; rebird oraz tworzenie aplikacji wielowarstwo-
wych, które można w łatwy sposób zintegrować
z innymi rozwiązaniami, takimi jak interfejsy
Web 2.0 wykorzystujące język JavaScript.
W Sieci Zapraszamy do drugiej części artykułu
http://www.bsc.com.pl/sdj/delphi2010/cz2/.
• Strona Delphi 2010 – http://www.embarcadero.com.pl/produkty/delphi/;
• Strona C++Builder 2010 – http://www.embarcadero.com.pl/produkty/cbuilder/;
• Strona RAD Studio 2010 – http://www.embarcadero.com.pl/produkty/rad-studio/; BOGDAN POLAK
• Pobieranie darmowych wersji testowych (wersje 30 dniowe) – http://www.embarcadero.com/
Autor od 5 lat jest konsultantem i trenerem w firmie
downloads.
BSC Polska, gdzie zajmuje się między innymi wspar-
ciem i prezentacjami technicznymi oraz prowadze-
niem szkoleń związanych ze środowiskami Del-
RTTI i refleksja phi i C++Builder. Równocześnie od kilkunastu lat
RTTI to skrót od słów Run Time Type Information. Jest to mechanizm informowania o typach
działający w trakcie uruchomienia programu, często nazywany również mechanizmem re- jest programistą, pasjonatem i aktywnym człon-
fleksji. Pozwala on wpływać na kod aplikacji już po jej uruchomieniu. Języki kompilowane kiem społeczności Delphi – kilkukrotnie brał udział
zamieniają kod źródłowy i użyte w nim typy danych na postać binarną, w której tracimy in- w spotkaniach programistów Delphi, gdzie prowa-
formacje o strukturze i znaczeniu bloku bajtów. Aby uzupełnić ten brak, większość nowocze- dził wykłady i prezentacje narzędzi. Ponadto zajmu-
snych języków programowania oferuje jakiś mechanizm informowania o typach. Pozwala on
je się obszarem ALM, specjalizuje się w inżynierii wy-
pisać aplikacje w sposób o wiele bardziej elastyczny dzięki funkcjom sprawdzania typów, za-
miany reprezentacji binarnej na postać języka wysokiego poziomu, możliwości wykrycia i magań oraz utrzymaniu jakości w projektach pro-
wywołania dowolnej metody czy ustawienia dowolnej własności. gramistycznych.
Kontakt z autorem: bogdan.polak@bsc.com.pl

44 12/2009
Programowanie urządzeń mobilnych

J2ME:
Bluetooth i MMAPI
Czyli Bluetooth i MMAPI w jednym stali domu
Niewiele osób zdaje sobie sprawę z możliwości swoich telefonów
komórkowych. W tym artykule zaznajomimy się z dwiema bibliotekami
dostępnymi dla platformy J2ME, które posiadają ogrom możliwych
zastosowań.
mobilnej, jest to, że jego implementacja za-
Dowiesz się: Powinieneś wiedzieć: wiera bardzo wiele błędów. Wystarczy przej-
• W jaki sposób wykorzystać Bluetooth do ko- • Jak programować w języku Java; rzeć fora o tematyce programistycznej, gdzie
munikacji w aplikacjach mobilnych J2ME; • Jak napisać prosty MIDlet; uczestnicy dyskutują, jak napisać aplika-
• W jaki sposób nagrywać oraz odtwarzać • Jak używać wysokopoziomowego API inter- cje mobilną, która ma wykorzystywać Blu-
dźwięk przy pomocy MMAPI w aplikacjach fejsu użytkownika w J2ME. etooth do przesyłu danych. Wielu z uczest-
J2ME; ników dyskusji na takich forach uważa na-
• W jaki sposób połączyć te dwie funkcje. wet, że jest to niemożliwe lub graniczy to
z cudem. Są jednak w błędzie, jest to moż-
liwe (wiele firm produkujących np. gry z po-
artykule. Przedstawię praktyczne użycie wodzeniem wykorzystuje tę technologie do
JSR-82 oraz JSR-135, czyli Bluetooth API tworzenia gier multiplayer), co nie oznacza,
Poziom oraz Mobile Multimedia API, na przykła- że jest to łatwe. Choć implementacja sama
trudności dzie prostej aplikacji działającej podobnie w sobie jest prosta, większe problemy spra-
jak walkie-talkie. wia porting takich aplikacji, tak aby działały
pomiędzy telefonami różnych producentów
O Bluetooth słów kilka oraz w różnych konfiguracjach klient-ser-

W
dzisiejszych czasach prawie Bluetooth jest obecnie mocno rozpo- wer. Nieraz możemy spotkać się z bardzo
każda osoba posiada telefon ko- wszechniony. Jest obsługiwany przez ezoterycznymi błędami, do rozwiązania któ-
mórkowy z Javą. Niestety, więk- większość laptopów oraz urządzeń mo- rych trzeba podchodzić, używając siódme-
szości osób, nawet wielu programistom, Ja- bilnych. Dla użytkowników telefonów go zmysłu zamiast zdrowego rozsądku (do-
va mobilna kojarzy się jedynie z prostymi jest to praktycznie jedyna możliwość wy- tyczy to jednak całego J2ME, dzięki czemu
grami podobnymi do tych, w które grało się miany danych bez dodatkowych opłat. Po- życie programisty jest barwne oraz bardzo
w latach osiemdziesiątych. Gdyby spojrzeć zwala również połączyć ze sobą wiele róż- uduchowione).
na podstawowe możliwości J2ME, to teore- nych urządzeń, bez ciągnięcia za sobą kilo- Pozwoliłem sobie na przydługą dygresję,
tycznie można by się zgodzić z taką opinią metrów zbędnych kabli. Bluetooth oczy- powracam więc do sedna tego rozdziału. Za-
(ale tylko teoretycznie, niektóre gry mobil- wiście ma również swoje minusy, przede nim przedstawię właściwą implementację,
ne są dziełem sztuki samym w sobie). Jed- wszystkim jest bardzo zawodny. Często chciałbym jednak przedstawić podstawowe
nakże J2ME przez lata była rozwijana, nie połączenie pomiędzy urządzeniami jest wiadomości, które będą nam potrzebne.
tylko dzięki wprowadzaniu nowych pro- bardzo trudno nawiązać, a i transfer po- Bluetooth działa w paśmie 2.4 GHz. Na
fili MIDP, ale również dzięki implemen- zostawia wiele do życzenia. Jednym z mi- tej samej częstotliwości działa również np.
tacji JSR (Java Specification Requests), czy- nusów Bluetooth, z punktu widzenia Javy WiFi, które może zakłócać nasze połącze-
li dodatkowej funkcjonalności, która nie
jest wspierana przez standardową bibliote-
Szybki start: J2ME
kę Javy mobilnej. Istnieje wiele nowych bi- Aby rozpocząć pracę z J2ME, należy wykonać następujące czynności:
bliotek (JSR), które są zaimplementowa-
ne jedynie na niewielu telefonach na ryn- • Pobrać oraz zainstalować Netbeans IDE wspierające technologie J2ME ze strony http://
ku (a na jeszcze mniejszej liczbie popraw- www.netbeans.org/downloads/index.html;
• Stworzyć w Netbeans IDE nowy projekt Java ME -> Mobile Application;
nie działają). Jednak i te po latach zapewne
• W kreatorze odznaczyć opcje Create Hello MIDlet;
zostaną czymś oczywistym i będą wspierane • Po utworzeniu projektu dodajemy do niego klasę o nazwie WalkieTalkie poprzez me-
przez większość telefonów, jak dwa JSR któ- nu File -> New File..., jako kategorie pliku wybieramy MIDP oraz jako jego typ – MIDlet.
re chciałbym na przykładzie opisać w tym

46 12/2009
J2ME: Bluetooth i MMAPI

nie Bluetooth. Z racji tego, że w otoczeniu


urządzeń komunikujących się mogą wystę- Listing 1. Przykład użycia klasy Player
pować inne używające tej samej częstotliwo-
ści, zastosowano technikę Frequency Hopping import javax.microedition.media.Manager;
SpreadSpectrum (FHSS). Polega ona na tym, import javax.microedition.media.MediaException;
że urządzenia w trakcie komunikacji zmie- import javax.microedition.media.Player;
niają kanał, na którym przesyłają/odbierają import javax.microedition.media.PlayerListener;
dane. Pasmo, z którego korzysta Blueto-
oth, podzielone jest na 79 kanałów. Połą- public class SoundPlayer {
czenie zmienia swój kanał cały czas w trak- public static void startPlayback() throws IOException, MediaException {
cie komunikacji, pozostając na jednym ka- Player playbackPlayer = Manager.createPlayer("http://sample.wav",
nale przez 625 ms. Następny kanał do prze- "audio/x-wav");
skoku wyznaczany jest na bazie adresu urzą- playbackPlayer.realize();
dzeń oraz czasu. playbackPlayer.prefetch();
Role są tutaj odwrócone w porównaniu z playbackPlayer.start();
np. protokołem TCP. Rolę serwera przejmu- }
je Master, rolę klienta Slave, przy czym to Ma- }
ster nawiązuje połączenie z nasłuchującym
urządzeniem (Slave), odwrotnie niż zapewne
większość z nas przywykła. W dalszej części
artykułu serwerem będziemy nazywać urzą-
dzenie oczekujące na połączenie (węzeł Sla-
ve), a klientem urządzenie nawiązujące połą- �����
czenie (węzeł Master).
Sieć, w której znajduje się jeden Master, �����

nazywa się piconet. Sieć taka może składać
się z jednego węzła typu Master oraz sied- �
miu aktywnych węzłów typu Slave. Poje-
dyncze węzły Slave nie mogą komunikować
się ze sobą pośrednio, mogą to robić jedy-
nie za pośrednictwem węzła Master. Wspo- � � �
mniana wcześniej zmiana kanałów jest syn-
������ ����������� ������
chronizowana na bazie jego zegara oraz ad-
resów Bluetooth. � �
Innym rodzajem sieci jest sieć scatternet.
Jest to sieć, w której mamy wiele węzłów Ma- ����� �����
ster, które łączy wspólny węzeł Slave. Może on
przeprowadzać zmianę kanału tylko dla jed- ��������� ���������
nej sieci piconet jednocześnie, przez co prze-
znaczone jest mniej czasu na komunikację ����������
dla każdej sieci.
Dwie warstwy stosu Bluetooth, z który-
mi będziemy mieli bezpośrednio do czynie-
nia, pisząc naszą aplikację, to L2CAP (Logi-
cal Link Control and Adaption Protocol) oraz Rysunek 1. Schemat sieci piconet oraz scatternet
RFCOMM (Radio Frequency Communica-
tion).
Warstwa L2CAP przede wszystkim odpo-
wiada za fragmentacje i łączenie pakietów z
������� �������
warstw wyższych. Wynika to z tego, że łącze ������
warstwy niższej ACL (Asynchronous Connec- ������� �������
tionLess) może przesłać pakiet o maksymal-
nej wielkości 339 bajtów. L2CAP zezwala na
��������� ���������� �������
przesłanie pakietów o maksymalnej wielkości
65535 bajtów.
Warstwa RFCOMM jest warstwą wyższą ���������� �������� ���������� �������
od L2CAP. Emuluje ona standardowy port
szeregowy. Użycie tej warstwy jest o wiele wy-
godniejsze, niż bezpośrednie użycie L2CAP, ������������ ������������ ������
przy czym na niektórych urządzeniach mo-
żemy się spotkać z problemami, używając
RFCOMM, i zostaniemy zmuszeni do użycia
warstwy niższego poziomu. Rysunek 2. Schemat ilustrujący cykl życia obiektu klasy Player

www.sdjournal.org 47
Programowanie urządzeń mobilnych

Zanim dwa urządzenia będą mogły się ze nia zostaje utworzony klucz inicjalizacji oraz Po zakończeniu parowania klucz autoryza-
sobą komunikować, musi nastąpić ich wza- klucz autoryzacji, po czym następuje wza- cji jest przechowywany w obu urządzeniach,
jemna autoryzacja. Służą do tego dwa pro- jemna autoryzacja urządzeń. Na klucz inicja- dzięki czemu proces wiązania może odbyć się
cesy: wiązanie (bonding) oraz parowanie (pa- lizacji składa się hasło (podawane przez użyt- bez udziału użytkownika. Jest to szczegól-
iring). kownika), adresy Bluetooth urządzeń oraz lo- nie przydatne, kiedy dwa urządzenia komu-
Wiązanie opiera się na współdzielonym sowe liczby. Klucz ten służy jedynie do zaszy- nikują się między sobą dosyć często. Na pew-
kluczu autoryzacji, jeśli taki nie istnieje, mu- frowania informacji podczas przesyłania klu- no bardzo praktyczne będzie sparowanie te-
si zostać wygenerowany. W procesie parowa- cza autoryzacji. lefonów, na których będziemy testować naszą
aplikację (chyba że jesteśmy na tyle kreatyw-
Listing 2. Przykład nagrywania dźwięku ni, żeby z każdym nawiązywaniem połącze-
nia wymyślać ciekawe hasło).
import java.io.IOException; Urządzenie, które chce nawiązać połącze-
import java.io.OutputStream; nie z innym urządzeniem, musi przejść przez
import javax.microedition.media.Manager; dwa etapy: wyszukiwania urządzeń (device
import javax.microedition.media.MediaException; inquiry) oraz wyszukiwania serwisów (servi-
import javax.microedition.media.Player; ce search).
import javax.microedition.media.PlayerListener; Jeśli w pobliżu urządzenia wyszukujące-
import javax.microedition.media.control.RecordControl; go będą inne urządzenia, będzie otrzymywać
ono od nich informacje o adresie oraz zega-
public class SoundRecorder { rze. Posłużą one do późniejszego zsynchroni-
private static final String CAPTURE_URL = "capture://audio"; zowania przeskoków częstotliwości z wybra-
private static Player capturePlayer = null; nym urządzeniem, dzięki czemu będzie moż-
private static RecordControl captureControl = null; na przejść do wyszukiwania serwisów. Aby
wykryć inne urządzenie w pobliżu, musi być
public static void startRecording(OutputStream output) throws IOException, ono wykrywalne poprzez przejście do stanu
MediaException { skanowania zapytań (inquiry scan). W tym
capturePlayer = Manager.createPlayer(CAPTURE_URL); stanie urządzenie pozostaje dłużej na każ-
capturePlayer.realize(); dym kanale, co zwiększa prawdopodobień-
captureControl = (RecordControl) capturePlayer.getControl("RecordControl"); stwo wykrycia zapytania.
captureControl.setRecordStream(output); Oprócz tego wykorzystywane są kody IAC
captureControl.startRecord(); (Inquiry Access Code): LIAC (Limited Inqu-
capturePlayer.start(); iry Access Code) oraz GIAC (General Inqu-
} iry Access Code). Kod GIAC stosowany jest,
gdy urządzenie ma być wykrywalne przez ca-
public static void stopRecording() throws IOException { ły czas, kod LIAC z kolei używany jest, gdy
captureControl.commit(); urządzenie ma być wykrywalne przez okre-
capturePlayer.close(); ślony czas.
} Po zakończeniu wykrywania przechodzi-
} my do wyszukiwania serwisów. Służy do
tego protokół SDP (Service Discovery Pro-
Listing 3. Fragment klasy WalkieTalkie tocol). Urządzenia Bluetooth przechowują
public class WalkieTalkie extends MIDlet { informacje o serwisach w bazie SDDB (Se-
public static boolean DEBUG = true; rvice Discovery DataBase). Każdy rekord
public static int RECORDING_LENGTH = 3; // seconds (serwis) w tej bazie posiada wiele atrybu-
tów, nas jednak przede wszystkim intere-
private static WalkieTalkie instance; suje UUID (Universally Unique IDentifier),
który identyfikuje serwis i jest w zasadzie
private WalkieTalkieForm mainForm; jego adresem. Identyfikator ten, który z za-
private DebugForm debugForm; łożenia ma być zawsze unikalny, jest 128
bitowy. Istnieje również mniejszy wariant
public WalkieTalkie() { UUID, ale jest on zarezerwowany dla naj-
instance = this; częściej używanych serwisów, protokołów
mainForm = new WalkieTalkieForm(); oraz profili.
debugForm = new DebugForm(); W trakcie tworzenia aplikacji wygeneruje-
} my taki identyfikator. Będzie on potrzebny
do rozpoczęcia nasłuchu (w węźle typu Sla-
public void startApp() { ve) oraz do zidentyfikowania, czy wykryty
Display.getDisplay(this).setCurrent(mainForm); serwis jest tym poszukiwanym przez nas (w
} węźle typu Master).
Informacje te powinny rzucić więcej świa-
(...) tła na działanie Bluetootha oraz pozwolą nam
} stawić pierwsze kroki na drodze do utworze-
nia naszej upragnionej aplikacji.

48 12/2009
J2ME: Bluetooth i MMAPI

O MMAPI słów kilka Listing 4. Fragment klasy WalkieTalkieForm


Odgłosy aplikacji mobilnych zapewne
większości czytelników kojarzą się z iry- public class WalkieTalkieForm extends Form implements CommandListener {
tującymi piskami lub brzdękami w grach. private Command startCapture;
Jest to często (ale nie zawsze) dosyć krzyw- private Command startServer;
dzące skojarzenie, ponieważ w wielu apli- private Command startClient;
kacjach dźwięki są zadziwiająco miłe dla private Command stopProcessing;
ucha, utworzone nawet w tak prostym for- private Command showDebug;
macie jak MIDI. Swoją drogą telefony w private Command exitApplication;
dzisiejszych czasach (oraz aplikacje J2ME) private Gauge timerStatus;
obsługują wiele nowych formatów dźwię- private StringItem statusMessage;
ków, takich jak MP3 oraz AAC. Niestety
twórcy dalej są ograniczeni limitem roz- public WalkieTalkieForm() {
miaru aplikacji, więc stojąc przed wybo- super("Walkie Talkie");
rem: większa funkcjonalność albo lepsza // create ui
jakość dźwięków, najpewniej wybiorą tę startServer = new Command("Server", Command.SCREEN, 0);
pierwszą opcje. startClient = new Command("Client", Command.SCREEN, 1);
Podstawowe funkcje MMAPI to odtwa- startCapture = new Command("Capture", Command.OK, 0);
rzanie lub nagrywanie mediów, takich jak stopProcessing = new Command("Stop", Command.CANCEL, 0);
dźwięki lub filmy, oprócz tego mamy możli- showDebug = new Command("Debug", Command.SCREEN, 2);
wość robienia zdjęć. Jeszcze więcej możliwo- exitApplication = new Command("Exit", Command.EXIT, 0);
ści daje nam Advanced MMAPI, które jednak addCommand(exitApplication);
nie jest jeszcze powszechnie wspierane przez addCommand(showDebug);
urządzenia obsługujące J2ME. setConnectionCommands();
W naszym programie użyjemy dwóch setCommandListener(this);
funkcji MMAPI – odtwarzania i nagrywa- timerStatus = new Gauge("Media status", false, WalkieTalkie.RECORDING_
nia dźwięku. Omówię teraz tę pierwszą ope- LENGTH * 10, 0);
racje. statusMessage = new StringItem("Status", "Idle");
Dźwięk odtwarzamy przy użyciu instan-
cji klasy Player, którą możemy uzyskać po- append(statusMessage);
przez klasę Manager. Obiekt tej klasy może append(timerStatus);
znajdować się w pięciu stanach: UNREALIZED, }
REALIZED, PREFETCHED, STARTED oraz CLOSED. public void commandAction(Command c, Displayable d) {
Cykl życia obiektu klasy Player możemy if (c == startCapture) {
zobaczyć na Rysunku 2. // start capturing
Zanim dźwięk będzie mógł zostać od- WalkieTalkieProcessor.startRecording();
tworzony, Player musi przejść ze stanu } else if (c == startServer) {
UNREALIZED kolejno przez stany REALIZED disableCommands();
oraz PREFETCHED. if (BTServer.startServer()) {
Trzeba zwrócić uwagę na to, że raz za- setTicker("Listening...");
mknięty Player nie może być ponownie setStatus("Listening");
użyty. }
Oprócz możliwości zmiany stanu obiektu } else if (c == startClient) {
Player możemy nasłuchiwać różne zdarze- disableCommands();
nia z nim związane. Aby to zrobić, musimy if (BTClient.startConnecting()) {
zaimplementować interfejs PlayerListener setTicker("Connecting...");
oraz zarejestrować go w obiekcie Player. setStatus("Connecting");
Player może generować wiele różnych zda- }
rzeń, najważniejsze to: DEVICE_UNAVAILABLE } else if (c == stopProcessing) {
oraz END_OF_MEDIA; pozwalają one obsłużyć setTicker("Disconnecting...");
ewentualny błąd oraz poinformować o zakoń- WalkieTalkieProcessor.stopProcessing();
czeniu nagrywania. Zdarzenie END_OF_MEDIA } else if (c == showDebug) {
wywoływane jest, gdy odtwarzanie dźwięku Display.getDisplay(WalkieTalkie.getInstance()).setCurrent(WalkieTalkie.
dobiegło końca. Z kolei DEVICE_UNAVAILABLE getDebugForm());
informuje nas, że urządzenie nie mogło z ja- } else if (c == exitApplication) {
kiejś przyczyny odtworzyć dźwięku lub mu- WalkieTalkie.getInstance().notifyDestroyed();
siało przerwać odtwarzanie. Dla przykła- WalkieTalkie.getInstance().destroyApp(true);
du, wiele telefonów informuje o zdarzeniu }
DEVICE_UNAVAILABLE, gdy podczas odtwarza- }
nia dźwięku przychodzi SMS, który odgrywa
własną melodię (na niektórych urządzeniach (...)
jest to jedyny sposób, aby wykryć takie prze- }
rwanie).

www.sdjournal.org 49
Programowanie urządzeń mobilnych

Przykład najprostszego użycia klasy Player niej jest wykonywać to tak, jak w podanym sza aplikacja jako walkie-talkie będzie tro-
możemy zobaczyć na Listingu 1. przykładzie. chę ułomna, ponieważ będzie umożliwia-
W naszym przykładzie jawnie zmienia- Muszę zmartwić czytelników jednym z ła przesyłanie tylko trzysekundowych na-
liśmy stany obiektu Player, nie jest to jed- minusów MMAPI – nie mamy możliwości grań. Na szczęście aplikacja oraz ten arty-
nak konieczne. Jeśli Player jest w stanie odtwarzania dźwięku na żywo. Nie może- kuł ma służyć jako zachęta do samodziel-
UNREALIZED, wywołanie metody start() my przez to przy pomocy tej biblioteki od- nego zgłębiania możliwości J2ME, nie ma-
niejawnie spowoduje przejście przez sta- twarzać np. internetowego radia; próba wy- my zamiaru stworzyć powszechnie używa-
ny REALIZED oraz PREFETCHED, choć na nie- konania tego sprawiłaby, że nasz Player nie nego produktu (przecież do rozmów słu-
których starszych urządzeniach może to mógłby przejść do stanu PREFETCHED. Nie- ży sam telefon, a ceny połączeń nie są już
powodować problemy, dlatego bezpiecz- stety przez brak takiej funkcjonalności na- tak drogie jak kiedyś). Aby zachęcić do sa-
modzielnych eksperymentów, mogę powie-
Listing 5. Fragment klasy SoundPlayer
dzieć, że teoretycznie można zasymulować
ciągłość naszego nagrania poprzez nagrywa-
public class SoundPlayer implements PlayerListener { nie np. jednosekundowych nagrań jedno po
drugim i przesyłaniu ich w tle do drugiego
private static Player playbackPlayer = null; węzła. Trudność będzie polegała na wyele-
minowaniu przerw pomiędzy nagraniami.
public static boolean startPlayback(InputStream input) throws IOException, W trakcie nagrywania nie będziemy mogli
MediaException { odtwarzać otrzymanego dźwięku, ale z te-
if (playbackPlayer == null) { go, co mi wiadomo, tak działa również tra-
playbackPlayer = Manager.createPlayer(input, "audio/x-wav"); dycyjne walkie-talkie. Zachęcam do realiza-
playbackPlayer.addPlayerListener(new SoundPlayer()); cji tego pomysłu!
playbackPlayer.realize(); Przejdźmy teraz do nagrywania dźwięku.
playbackPlayer.prefetch(); Do tego celu również będzie potrzebna nam
playbackPlayer.start(); klasa Player, jako adres dźwięku użyjemy
return true; URL capture://audio, wskazuje on na urządze-
} else { nie przechwytujące dźwięk. Dla niektórych
return false; urządzeń adres ten może być inny, wtedy
} musimy zajrzeć do dokumentacji producen-
ta telefonu (np. niektóre telefony mają dwie
} kamery, wtedy URL dla drugiej kamery mo-
że być inny niż capture://video). Gdy utworzy-
public static boolean stopPlayback() { my już obiekt Player, musimy ustalić stru-
if (playbackPlayer != null) { mień, do którego będzie trafiało nasze na-
if (playbackPlayer.getState() == Player.STARTED) { granie. Możemy zrobić to przy pomocy kla-
try { sy RecordControl, której obiekt możemy po-
playbackPlayer.stop(); brać przy pomocy metody getControl() kla-
} catch (MediaException ex) { sy Player.
ex.printStackTrace(); Aby przerwać nagrywanie, musimy wy-
} wołać metodę commit() w instancji klasy
} RecordControl. Spowoduje to zapisanie na-
if (playbackPlayer.getState() != Player.CLOSED) { grania do wskazanego wcześniej strumienia,
playbackPlayer.close(); po czym wywołujemy metodę stop() lub
} close() klasy Player. Najlepiej zilustruje to
} przykład ukazany na Listingu 2.
playbackPlayer = null; W podanym przykładzie metoda
return true; startRecording() rozpocznie nagrywanie
} dźwięku do strumienia podanego jako argu-
ment. Gdy będziemy chcieli zaprzestać na-
public void playerUpdate(Player player, String event, Object eventData) { grywania, wystarczy, że wywołamy meto-
if (event == END_OF_MEDIA || event == DEVICE_UNAVAILABLE) { dę stopRecording(), która zapisze dźwięk
playbackPlayer.close(); do strumienia oraz zamknie obiekt klasy
playbackPlayer = null; Player.
}
} Struktura aplikacji
Teraz, gdy mamy podstawową wiedzę o
public static boolean isPlaying() { dwóch technologiach, których mamy zamiar
return playbackPlayer != null; użyć, możemy przejść do dzieła!
} Rozpocznę od opisu klas, jakie będą utwo-
rzone w naszej aplikacji:
(...)
} • BTClient.java – klasa odpowiedzialna
za komunikację węzła Master;

50 12/2009
J2ME: Bluetooth i MMAPI

• BTServer.java – klasa odpowiedzialna czynny oczekujemy na polecenie użytkow- Odtwarzamy dźwięk!


za komunikację węzła Slave; nika (uruchomienie serwera lub próba po- Klasa odtwarzająca dźwięk w zasadzie jest bar-
• DebugForm.java – klasa odpowiedzial- łączenia się z serwerem), w stanie nawią- dzo prosta. Składa się z dwóch metod statycz-
na za wyświetlanie dziennika zda- zujący połączenie, zależnie od trybu, jaki nych, odpowiadających za odtwarzanie oraz za-
rzeń; wybraliśmy, będziemy oczekiwać na połą- trzymanie odtwarzania i zwolnienie zasobów po-
• Log.java – klasa logująca wydarzenia czenie lub próbować nawiązać połączenie trzebnych do odtworzenia dźwięku. Aby prze-
w dzienniku zdarzeń; z serwerem. chwytywać zdarzenia dotyczące instancji klasy
• SoundPlayer.java – klasa odpowiedzial- Z kolei w stanie połączony program może Player, będziemy musieli zaimplementować in-
na za odtwarzanie dźwięku; odtwarzać lub nagrywać dźwięk. Tak przed- terfejs PlayerListener. Fragment klasy odtwa-
• SoundRecorder.java – klasa odpowie- stawia się szkielet naszej aplikacji, teraz mo- rzającej dźwięk widoczny jest na Listingu 5.
dzialna za nagrywanie dźwięku; żemy przejść do ciekawszego tematu, miano- Metoda startPlayback() kolejno two-
• WalkieTalkie.java – MIDlet naszej wicie odtwarzania dźwięku. rzy instancje klasy Player, używając stru-
aplikacji;
• WalkieTalkieForm.java – klasa odpo- Listing 6. Fragment klasy SoundRecorder
wiedzialna za interfejs naszej aplikacji;
• WalkieTalkieProcessor.java – najważ- public class SoundRecorder implements PlayerListener {
niejsza klasa naszej aplikacji, odpowie-
dzialna za logikę naszej aplikacji. private static final String CAPTURE_URL = "capture://audio";
private static Player capturePlayer = null;
Program był przetestowany i działał bezpro- private static RecordControl captureControl = null;
blemowo na telefonach: Nokia 7370, Nokia
5800 Xpress Music, Nokia E61i oraz No- public static boolean startRecording(OutputStream output) throws IOException,
kia E71. Pisząc naszą aplikację, musimy za- MediaException {
cząć od punktu startowego, czyli MIDle- if (capturePlayer == null) {
tu WalkieTalkie . Rola tej klasy w aplikacji capturePlayer = Manager.createPlayer(CAPTURE_URL);
ogranicza się w zasadzie do utworzenia klasy capturePlayer.addPlayerListener(new SoundRecorder());
instancji DebugForm oraz WalkieTalkieForm . capturePlayer.realize();
Fragment kodu klasy WalkieTalkie możemy captureControl = (RecordControl) capturePlayer.getControl("RecordCont
ujrzeć na Listingu 3. rol");
Oprócz tego nasza klasa posiada stałą captureControl.setRecordStream(output);
DEBUG, która umożliwia prowadzenie dzien- captureControl.startRecord();
nika zdarzeń, oraz stałą RECORDING_LENGTH, capturePlayer.start();
która określa (w sekundach), jak długie na- return true;
grania ma przechwytywać aplikacja. Za } else {
główny interfejs programu odpowiada kla- return false;
sa WalkieTalkieForm, jej fragment możemy }
zobaczyć na Listingu 4.
Klasa WalkieTalkie rozszerzająca klasę }
Form zawiera sześć obiektów klasy Command,
które zezwalają na interakcje z naszym pro- public static boolean stopRecording() throws IOException {
gramem: if (capturePlayer != null) {
try {
• startServer() – powoduje wystarto- captureControl.commit();
wanie serwera oraz rozpoczęcie nasłu- capturePlayer.close();
chu (węzeł typu Slave); } catch (IOException e) {
• startClient() – powoduje rozpoczęcie throw e;
wyszukiwania urządzeń typu Slave oraz } finally {
nawiązanie połączenia ,jeśli takie urzą- captureControl = null;
dzenie zostanie znalezione (węzeł typu capturePlayer = null;
Master); }
• startCapture() – powoduje rozpoczę- return true;
cie nagrywania dźwięku; } else {
• stopProcessing() – zamyka połącze- return false;
nie, jeśli jest ono nawiązane; }
• showDebug() – otwiera ekran z dzienni- }
kiem zdarzeń;
• exitApplication() – zamyka aplikację. public void playerUpdate(Player player, String event, Object eventData) {
Log.message("SoundRecorder: " + event);
Możliwość wywołania tych metod zależy od }
stanu, w którym jest program.
Ogólnie nasza aplikacja może znajdować (...)
się w trzech stanach: bezczynny, nawiązują- }
cy połączenie oraz połączony. W stanie bez-

www.sdjournal.org 51
Programowanie urządzeń mobilnych

mienia przekazanego jako argument, reje- blemy z odtwarzaniem lub nagranie skoń- Nagrywamy dźwięk
struje PlayerListener , tworząc instancje czyło się w metodzie playerUpdate(), któ- Klasa odpowiedzialna za przechwytywanie
samej siebie oraz rejestrując ją, a następnie ra pochodzi z interfejsu PlayerListener. dźwięku jest tylko troszeczkę bardziej rozbudo-
wymusza przejście przez stany REALIZED, Oprócz tego mamy metodę isPlaying(), wana od klasy SoundPlayer. Fragment jej kodu
PREFETCHED oraz STARTED. Jeśli po drodze którą możemy sprawdzić, czy dźwięk jest można zobaczyć na Listingu 6. Składa się ona je-
nie napotkamy żadnych problemów, z gło- odtwarzany (zakładamy, że dźwięk jest od- dynie z trzech metod: startRecording() (rozpo-
śnika telefonu powinien wydobyć się upra- twarzany, gdy istnieje instancja klasy Player czyna nagrywanie dźwięku), stopRecording()
gniony dźwięk. Metoda stopPlayback() za- – w naszej aplikacji w zasadzie nie ma in- (zatrzymuje nagrywanie dźwięku) oraz znanej
trzymuje odtwarzanie dźwięku (jeśli jest od- nej możliwości). Tak prezentuje się kla- nam już metody playerUpdate(), pochodzącej
twarzany), zamyka Player oraz zwalnia jego sa odpowiedzialna za odtwarzanie dźwię- z interfejsu PlayerListener.
referencje. Instancji klasy Player pozbywa- ku. Przejdźmy teraz do jeszcze ciekawsze- Nasza metoda startRecording(), podob-
my się również w przypadku, gdy zaszły pro- go tematu. nie jak metoda startPlayback(), tworzy in-

Listing 7. Fragment klasy BTServer

public class BTServer implements Runnable { device.setDiscoverable(DiscoveryAgent.GIAC);

private static StreamConnectionNotifier serverSocket; serverSocket = (StreamConnectionNotifier) Connect


or.open(WalkieTalkieProcessor.URL);
public static boolean startServer() { ServiceRecord sr = device.getRecord(serverSoc
if (serverSocket != null) { ket);
try { sr.setAttributeValue(0x0008, new DataElement(Da
serverSocket.close(); taElement.U_INT_1, 0xFF));
} catch (IOException ex) {
} StreamConnection connection = serverSocket.acce
serverSocket = null; ptAndOpen();
} WalkieTalkieProcessor.clientConnected(connect
ion);
Thread thr = new Thread(new BTServer()); } catch (Exception e) {
thr.start(); Log.message("BTServer.run(): " +
e.getClass().getName() + ": " +
return true; e.getMessage());
} }
}
public void run() {
try { (...)
LocalDevice device = null; }
device = LocalDevice.getLocalDevice();

Listing 8. Fragment klasy BTClient

public class BTClient implements DiscoveryListener, Runnable devicesList = new Vector();


{ servicesList = new Vector();
inquiryCompleted = false;
private static DiscoveryAgent discoveryAgent; searchCompleted = false;
private static BTClient btclientInstance; try {
private static boolean inquiryCompleted; LocalDevice device = LocalDevice.getLocalDevi
private static boolean searchCompleted; ce();
private static Vector devicesList; discoveryAgent = device.getDiscoveryAgent();
private static Vector servicesList; performInquiry();
performServiceSearch();
public static boolean startConnecting() { connectToServer();
btclientInstance = new BTClient(); } catch (Exception e) {
Thread thr = new Thread(btclientInstance); connectionFailed(e.getMessage());
thr.start(); }
}
return true;
} (...)
}
public void run() {

52 12/2009
J2ME: Bluetooth i MMAPI

stancję klasy Player, jednak jako argument Nawiązanie połączenia od strony serwe- wiązywane jest połączenie, jednak oprócz te-
podaje nie strumień, a URL do urządze- ra, jak widać, jest bardzo proste. W przypad- go tworzy i zapamiętuje również referencje
nia przechwytującego dźwięk (capture://au- ku klienta będzie to odrobinę bardziej skom- do instancji klasy, w której się znajduje. Jest
dio). Po utworzeniu obiektu Player, meto- plikowane. to konieczne, ponieważ jak się później oka-
da tworzy instancję klasy SoundRecorder że będziemy potrzebowali obiektu klasy im-
i rejestrują ją jako PlayerListener w obiek- Wyszukujemy serwer plementującej interfejs DiscoveryListener,
cie klasy Player. Następnie musimy po- Jak już wiemy, nawiązanie połączenia z ser- który jest właśnie implementowany przez
brać obiekt RecordControl, przy pomo- werem (węzłem typu Slave) wymaga przej- klasę BTClient.
cy którego określimy strumień, do które- ścia przez kilka etapów, dlatego też kod omó- W wątku musimy pobrać obiekt kla-
go będzie trafiało nagranie. Oczywiście bę- wię w kilku częściach. Najpierw przedstawię sy DiscoveryAgent pośrednio przez klasę
dzie to strumień output, podany jako ar- główną metodę, którą widać na Listingu 8. LocalDevice – on również przyda się w póź-
gument. Kiedy strumień zostanie określo- Jak widać, metoda startConnecting() niejszych etapach. Zwróćmy uwagę na to, że
ny, możemy rozpocząć nagrywanie, wy- uruchamia oddzielny wątek, w którym na- nie musimy przenosić urządzenia w stan wy-
wołując metody startRecord() w instan-
cji RecordControl oraz start() w obiekcie
Listing 9. Fragment klasy BTClient
Player. Metoda stopRecording(), oprócz
zatrzymania nagrywania w instancji kla-
sy Player, musi wywołać metodę commit() public class BTClient implements DiscoveryListener, Runnable {
obiektu RecordControl, która zapewnia
właściwe zakończenie nagrywania. Metoda private static DiscoveryAgent discoveryAgent;
playerUpdate() nie odpowiada za nic kon- private static BTClient btclientInstance;
kretnego, a jedynie ogranicza się do informo- private static boolean inquiryCompleted;
wania o zmianach w obiekcie klasy Player. private static Vector devicesList;
Tak prezentują się nasze dwie klasy odpo-
wiadające za dźwięk. public synchronized void deviceDiscovered(RemoteDevice remoteDevice,
DeviceClass deviceClass) {
Uruchamiamy serwer if ((deviceClass.getMajorDeviceClass() == 0x200) && !devicesList.contains(
Teraz przedstawię, jak uruchomimy nasz ser- remoteDevice)) {
wer. Pamiętajmy, że nazywamy to umownie devicesList.addElement(remoteDevice);
serwerem, w rzeczywistości jest to węzeł ty- }
pu Slave, czyli klient w realiach technologii }
Bluetooth. Nazywamy go serwerem tylko ze
względu na analogie do połączeń TCP. public synchronized void inquiryCompleted(int discType) {
Metodę odpowiedzialną za rozpoczęcie na- inquiryCompleted = true;
słuchu widzimy na Listingu 7. synchronized (BTClient.class) {
Metoda startServer() uruchamia wą- BTClient.class.notify();
tek, w którym rozpoczyna się właściwy }
nasłuch. }
Pierwsze, co musimy zrobić, to pobranie
instancji klasy LocalDevice, która pozwoli private void performInquiry() throws Exception {
zmienić stan urządzenia bluetooth na stan synchronized (BTClient.class) {
wykrywalny (GIAC), dzięki czemu klienci discoveryAgent.startInquiry(DiscoveryAgent.GIAC, btclientInstance);
będą mogli odnaleźć nasz serwer. Następnie while (!inquiryCompleted) {
pobieramy gniazdo, przez które będziemy try {
mogli odbierać połączenia. Do tego celu wy- BTClient.class.wait();
korzystamy metodę open() klasy Connector } catch (InterruptedException e) {
z odpowiednim argumentem, to jest adresem }
URL naszego serwera. }
Teraz pobierzemy rekord serwisu oraz }
zmienimy jego atrybut określający dostęp-
ność serwisu (ServiceAvailability). Zmienimy if (devicesList.size() == 0) {
jego wartość na 0xFF, co oznacza, że nasz ser- throw new Exception("No devices found");
wis jest w pełni dostępny. Następnie przecho- } else {
dzimy do właściwego nasłuchu. Jeśli klient Log.message("BTClient.startInquiry(): Found " + devicesList.size() + "
połączy się z serwerem, zostanie o tym po- device(s)");
wiadomiona główna klasa odpowiedzialna za WalkieTalkie.getMainForm().setStatus("Found " + devicesList.size() + "
logikę aplikacji. device(s)");
Oczywiście po drodze może zostać rzuco- }
ny wyjątek taki, jak np. SecurityException, }
w przypadku gdy użytkownik aplikacji nie
zgodzi się na połączenie, lub IOException (...)
w przypadku jakichkolwiek problemów z sa- }
mym połączeniem.

www.sdjournal.org 53
Programowanie urządzeń mobilnych

krywalny, w końcu to my wyszukujemy ser- dany serwis został znaleziony. W razie ja- Spójrzmy na metodę performInquiry().
wer, nie serwer nas. kichkolwiek niepowodzeń rzucony zosta- Jest ona odpowiedzialna za wyszukanie
Proces połączenia podzielony jest na trzy nie wyjątek, który zostanie przekazany do urządzeń w otoczeniu. Jak widać na po-
metody odpowiedzialne kolejno za: wy- interfejsu użytkownika poprzez metodę czątku, przechodzimy w synchronizowany
szukiwanie urządzeń w otoczeniu, wyszu- connectionFailed(). blok, w którym uruchamiamy wyszukiwa-
kanie serwisów na znalezionych urządze- Zobaczmy, jak wygląda wyszukiwanie nie przy pomocy metody startInquiry()
niach, nawiązaniu połączenia, jeśli pożą- urządzeń na Listingu 9. obiektu klasy DiscoveryAgent, który pobra-
liśmy wcześniej z klasy LocalDevice. Me-
Listing 10. Fragment klasy BTClient
toda startInquiry() jest asynchronicz-
na, musimy więc w jakiś sposób dowiedzieć
public class BTClient implements DiscoveryListener, Runnable { się, że wyszukiwanie się zakończyło. Posłu-
ży nam do tego zmienna inquiryCompleted
private static DiscoveryAgent discoveryAgent; – dopóki będzie miała ona wartość false,
private static BTClient btclientInstance; będziemy usypiać aktualny wątek oraz cze-
private static boolean searchCompleted; kać na wznowienie. Proces wyszukiwania
private static Vector servicesList; urządzeń komunikuje się z nami przy po-
mocy metod deviceDiscovered() oraz
public synchronized void servicesDiscovered(int transID, ServiceRecord[] inquiryCompleted(), które pochodzą z za-
serviceRecords) { implementowanego wcześniej w klasie
for (int i = 0; i < serviceRecords.length; i++) { BTClient interfejsu DiscoveryListener.
servicesList.addElement(serviceRecords[i]); Właśnie do tego była nam potrzebna instan-
} cja klasy BTClient.
} Metoda deviceDiscovered() wywoły-
wana jest, gdy w pobliżu zostanie wykry-
public synchronized void serviceSearchCompleted(int transID, int respCode) { te urządzenie. W metodzie tej sprawdza-
searchCompleted = true; my klasę znalezionego urządzenia (0x200
synchronized (BTClient.class) { oznacza telefon komórkowy), dzięki cze-
BTClient.class.notify(); mu na tym etapie możemy odrzucić urzą-
} dzenia, na których nasz serwer na pewno
} nie będzie działał. Do tego sprawdzamy,
czy urządzenie nie zostało już wcześniej
private void performServiceSearch() throws Exception { wykryte – jeżeli nie, dodajemy go do na-
final Enumeration devicesEnum = devicesList.elements(); szego wektora przechowującego urządze-
while (devicesEnum.hasMoreElements()) { nia. Metoda inquiryCompleted() wywoły-
RemoteDevice remoteDevice = (RemoteDevice) devicesEnum.nextElement(); wana jest, kiedy wyszukiwanie zostaje za-
synchronized (BTClient.class) { kończone. W metodzie tej możemy zmie-
final int[] attrbs = {0x100, 0x101, 0x102}; nić wartość naszej zmiennej oznaczającej
discoveryAgent.searchServices(attrbs, new UUID[]{new UUID zakończenie wyszukiwania oraz obudzić
(WalkieTalkieProcessor.UUID, false)}, remoteDevice, uśpiony wątek.
btclientInstance); W tym momencie powinniśmy z powrotem
while (!searchCompleted) { znaleźć się w metodzie performInquiry().
try { Pozostaje sprawdzić, czy zosta-
BTClient.class.wait(); ły odnalezione jakiekolwiek urządze-
} catch (InterruptedException e) { nia, a jeśli tak, to możemy przejść da-
} lej. Następnie wywoływana jest meto-
} da performServiceSearch(), której kod
} można zobaczyć na Listingu 10. Przeana-
} lizujmy metodę performServiceSearch().
Jak widać, iterujemy po znalezionych
if (servicesList.size() == 0) { wcześniej urządzeniach. Musimy na każ-
Log.message("BTClient.startServiceSearch(): No services found"); dym z nich przeprowadzić operację wy-
throw new Exception("No services found"); szukiwania serwisu. Podobnie jak w me-
} else { todzie performInquiry(), przechodzi-
Log.message("BTClient.startServiceSearch(): Found " + my w blok synchronizowany, uruchamia-
servicesList.size() + " service(s)"); my wyszukiwanie przy pomocy metody
WalkieTalkie.getMainForm().setStatus("Found " + servicesList.size() + searchServices() klasy DiscoveryAgent.
" service(s)"); Metoda ta jest również asynchroniczna,
} więc będziemy potrzebowali zmiennej
oznaczającej zakończenie wyszukiwania,
} posłuży do tego zmienna searchCompleted.
(...) Oczywiście zanim będzie miała ona war-
} tość true, będziemy usypiać działający
wątek. Skupmy się na wywołaniu meto-

54 12/2009
J2ME: Bluetooth i MMAPI

dy searchServices(), tak wygląda jej de-


klaracja: Listing 11. Fragment klasy BTClient

searchServices(int[] attrSet, UUID[] public class BTClient implements DiscoveryListener, Runnable {


uuidSet, private static Vector servicesList;
RemoteDevice btDev, private void connectToServer() throws Exception {
DiscoveryListener ServiceRecord sr = (ServiceRecord) servicesList.elementAt(0);
discListener) String url = sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT,
false);
Argument attrSet zawiera identyfikato- StreamConnection connection = (StreamConnection) Connector.open(url);
ry atrybutów serwisu, jakie mają zostać WalkieTalkieProcessor.serverConnected(connection);
pobrane (w naszym przypadku kolejno }
ServiceName , ServiceDescription oraz (...)
ProviderName ), następnie uuidSet mu- }
si zawierać listę UUID, które są używa-
ne przez poszukiwane serwisy. Argument Listing 12. Fragment klasy WalkieTalkieProcessor
btDev oznacza urządzenie, na którym chce-
my dokonać wyszukiwania, a argument public class WalkieTalkieProcessor {
discListener to oczywiście klasa imple- private static boolean process = false;
mentująca interfejs DiscoveryListener (w private static StreamConnection connection = null;
naszym przypadku klasa BTClient). Gdy private static DataOutputStream output = null;
serwis (lub serwisy) odpowiadający wyma- private static DataInputStream input = null;
ganiom zostanie znaleziony, zostaje wywo- public static void clientConnected(StreamConnection _connection) {
łana metoda servicesDiscovered(). W jej connection = _connection;
ciele dodajemy znalezione serwisy do na- WalkieTalkie.getMainForm().setStatus("Client successfully connected");
szego wektora. Ostatecznie w naszym przy- WalkieTalkie.getMainForm().setTicker("Client connected");
padku zakończymy wyszukiwanie, ma- startProcessing();
jąc tylko jeden znaleziony serwis (chyba że }
cierpimy na nadmiar telefonów w otocze- public static void serverConnected(StreamConnection _connection) {
niu, na których uruchomimy naszą aplika- connection = _connection;
cję). Kiedy wyszukiwanie zostanie zakoń- WalkieTalkie.getMainForm().setStatus("Successfully connected to server");
czone, zostaje wywołana metoda service WalkieTalkie.getMainForm().setTicker("Connected to server");
SearchCompleted(). Podobnie jak przy wy- startProcessing();
szukiwaniu urządzeń, ustawiamy naszą }
zmienną oznaczającą zakończenie wyszuki- private static void startProcessing() {
wania na true oraz budzimy uśpiony wątek. // disable server/client buttons
W tym momencie powinniśmy być zno- WalkieTalkie.getMainForm().setCaptureCommand();
wu w metodzie performServiceSearch(). try {
Jeśli nasz wektor przechowuje jeszcze ja- input = connection.openDataInputStream();
kieś urządzenia, zostanie uruchomio- output = connection.openDataOutputStream();
ny nowy proces wyszukiwania serwisu. } catch (IOException e) {
Kiedy przeszukamy wszystkie urządze- Log.message("WalkieTalkieProcessor.startThreads(): IOException: " +
nia, pozostaje nam sprawdzić, czy znaleź- e.getMessage());
liśmy jakikolwiek serwis. Jeśli odnaleźli- }
śmy chociaż jeden, możemy nawiązać po- process = true;
łączenie. Zostanie to wykonane w meto- Thread inputThread = new Thread() {
dzie connectToServer(), którą widać na Li- public void run() {
stingu 11. inputUpdate();
Pobieramy pierwszy rekord z listy znale- }
zionych serwisów oraz jego URL. Teraz wy- };
starczy użyć metody open klasy Connector i Thread outputThread = new Thread() {
już mamy nasze upragnione połączenie! Te- public void run() {
raz możemy je przekazać do klasy, która wy- outputUpdate();
konują całą czarną robotę. }
};
Łączymy wszystko w całość! inputThread.start();
Przechodzimy teraz do serca naszej apli- outputThread.start();
kacji, klasy WalkieTalkieProcessor, któ- }
ra odpowiada za odbiór i odtwarzanie oraz
nagrywanie i wysyłanie dźwięku. Zacznij- (...)
my od krótkiego opisu klasy. Samo działa- }
nie tej klasy powinno być identyczne, nie-
zależnie, czy nasze urządzenie to serwer lub

www.sdjournal.org 55
Programowanie urządzeń mobilnych

klient – obie strony mają takie same moż- gram zachowa się analogicznie, gdy zechce- ją użytkownika oraz uruchomiają metodę
liwości. Po nawiązaniu połączenia, nieważ- my nagrywać dźwięk w trakcie odtwarzania startProcessing(). Metoda ta uruchamia
ne, czy z klientem, czy z serwerem, urucha- odebranego nagrania, czyli poczeka, aż zo- dwa opisane wcześniej wątki, jednak zanim
miamy dwa wątki: jeden odpowiedzialny za stanie ono zakończone. się tak stanie, musimy otworzyć strumienie
oczekiwanie na dane oraz odtwarzanie ich, Spójrzmy najpierw na kod odpowie- wejścia/wyjścia. Gdy to zrobimy, ustawiamy
oraz drugi – odpowiedzialny za nagrywa- dzialny za uruchomienie naszych wąt- zmienną process na true, będzie ona infor-
nie dźwięku (oczywiście jeśli użytkownik ków, widoczny na Listingu 12. Spójrzmy mowała wątki, czy kontynuować, czy może
tego będzie chciał) oraz wysyłanie nagrania na dwie metody: serverConnected() oraz zakończyć pracę. Następnie tworzymy i star-
do drugiego urządzenia. Wątki te nie będą clientConnected(). Są wywoływane, gdy tujemy wątki. Najpierw wątek inputThread,
ze sobą kolidować, tzn. jeśli w trakcie nagry- połączenie Bluetooth zostanie nawiązane. Ich który wywoła metodę inputUpdate() odpo-
wania dźwięku otrzymamy nagranie od dru- działanie jest podobne, to znaczy: zapisują wiedzialną za odbiór i odtwarzanie dźwięku,
giego urządzenia, to zostanie ono odtworzo- obiekt klasy StreamConnection, który posłu- a potem wątek outputThread, który wywoła
ne dopiero po zakończeniu nagrywania. Pro- ży nam do otwarcia strumieni, powiadamia- metodę outputUpdate(), odpowiedzialną za
nagrywanie oraz przesyłanie dźwięku do dru-
Listing 13. Fragment klasy WalkieTalkieProcessor giego urządzenia.
Zanim opiszemy metody odpowiedzial-
public class WalkieTalkieProcessor { ne za odczyt/zapis dźwięku, musimy dowie-
dzieć się, jak wątki będą działać, nie wcho-
private final static Object playbackLock = new Object(); dząc sobie w drogę. Do synchronizacji wąt-
private final static Object recordingLock = new Object(); ków posłużymy się czterema metodami, któ-
private static boolean capturingSound = false; re można zobaczyć na Listingu 13.
private static boolean playingSound = false; Mamy tutaj dwa zestawy metod.
Pierwszy, waitForPlayback() oraz
private static void waitForPlayback() { waitForRecording(), odpowiada za za-
while (playingSound) { blokowanie wątku, z którego zostaną wy-
synchronized (playbackLock) { wołane te metody – odpowiednio do-
try { póki nie zostanie zakończone odtwarza-
playbackLock.wait(); nie lub nagrywanie. Drugi zestaw metod,
} catch (InterruptedException ex) { playbackEnded() oraz recordingEnded(),
} służy do powiadomienia oczekujących wąt-
} ków o tym, że operacja odtwarzania lub na-
} grywania zakończyła się. Do poprawnego
} działania tych metod będziemy potrzebo-
wali kilku zmiennych. Po pierwsze – po-
private static void waitForRecording() { trzebne będą obiekty, na których będziemy
while (capturingSound) { synchronizować nasze wątki. W naszym
synchronized (recordingLock) { programie użyjemy w tym celu dwóch
try { obiektów klasy Object: playbackLock oraz
recordingLock.wait(); recordingLock. Oprócz tego potrzebuje-
} catch (InterruptedException ex) { my dwóch zmiennych, które będą ozna-
} czać, czy nagrywanie lub odtwarzanie jest
} w toku. Użyjemy do tego dwóch zmien-
} nych typu boolean: capturingSound oraz
} playingSound. Mając te pomocnicze meto-
dy, możemy spokojnie zacząć implementa-
private static void playbackEnded() { cje dwóch działających w harmonii wątków.
synchronized (playbackLock) { Aby łatwiej zrozumieć, co odbieramy, mu-
playingSound = false; simy wiedzieć, co zostaje wysłane, zacznie-
SoundPlayer.stopPlayback(); my więc od opisu metody outputUpdate(),
playbackLock.notify(); zawartą w Listingu 14.
} Jak widzimy, metoda ta będzie wykony-
} wać się, dopóki zmienna process będzie
miała wartość true. Jeśli z jakiegoś powo-
private static void recordingEnded() { du użytkownik postanowi przerwać po-
synchronized (recordingLock) { łączenie, zmienna ta zostanie ustawio-
capturingSound = false; na na false, a nasza metoda wywoła me-
recordingLock.notify(); todę closeConnection(), która odpowie-
} dzialna jest za zamknięcie połączenia. Za-
} łóżmy, że process ma wartość true, dal-
szy przebieg będzie zależał od zmiennej
(...) capturingSound, która zostaje ustawio-
} na na true, gdy użytkownik zechce roz-
począć nagrywanie. Jeśli zmienna będzie

56 12/2009
J2ME: Bluetooth i MMAPI

miała wartość false, po prostu uśpimy


nasz wątek na sto milisekund. Powiedz- Listing 14. Fragment klasy WalkieTalkieProcessor
my, że użytkownik chce nagrać dźwięk,
przez co zmienna capturingSound ma war- public class WalkieTalkieProcessor {
tość true. Pierwsza rzecz, jaką wykonamy,
to wywołanie metody waitForPlayback(), private static boolean process = false;
dzięki temu ,jeśli jesteśmy w trakcie od- private static DataOutputStream output = null;
twarzania dźwięku, poczekamy aż zosta- private static boolean capturingSound = false;
nie to zakończone. Załóżmy, że odtwarza-
nie już się zakończyło i możemy już rozpo- private static void outputUpdate() {
cząć nagrywanie. W tym celu tworzymy in- while (process) {
stancje klasy ByteArrayOutputStream, bę- if (capturingSound) {
dzie to strumień, do którego będzie trafiał try {
nasz dźwięk. Pozostaje nam wywołać meto- waitForPlayback();
dę startRecording() klasy SoundRecorder WalkieTalkie.getMainForm().setStatus("Started recording");
, używając strumienia wyjściowego jako ar- ByteArrayOutputStream recordOutput = new
gumentu. Teraz możemy uśpić wątek na ByteArrayOutputStream();
czas nagrania, w programie robimy to w pę- SoundRecorder.startRecording(recordOutput);
tli for, żeby móc aktualizować wyświetla-
ny na ekranie pasek postępu, który umi- int value;
la czas nagrywania użytkownikowi. Kiedy for (value = 0; value < WalkieTalkie.RECORDING_LENGTH * 10;
nagraliśmy już tyle dźwięku, ile chcemy, value++) {
możemy zatrzymać nagrywanie metodą WalkieTalkie.getMainForm().setTimerValue(value);
stopRecording(). Teraz, gdy mamy dane, try {
musimy je jakoś przesłać. Najpierw pobie- Thread.sleep(100);
ramy tablice bajtów ze strumienia, następ- } catch (InterruptedException ex) {
nie wysyłamy do drugiego urządzenia war- }
tość typu int, która przechowuje informa- }
cje o wielkości nagrania. Będzie to potrzeb- WalkieTalkie.getMainForm().setTimerValue(WalkieTalkie.RECORDIN
ne drugiej stronie do alokacji pamięci po- G_LENGTH * 10);
trzebnej do odbioru nagrania. Kiedy już to SoundRecorder.stopRecording();
zrobimy, przesyłamy pobraną wcześniej ta-
blicę bajtów, czyścimy strumień oraz infor- WalkieTalkie.getMainForm().setStatus("Sending recording");
mujemy o tym fakcie użytkownika. Musi- byte[] recording = recordOutput.toByteArray();
my jeszcze powiadomić o tym fakcie wątek, Log.message("WalkieTalkieProcessor.outputUpdate(): Sending
który być może oczekujemy, aż nagranie zo- recording: " + recording.length);
stanie zakończone – w tym celu wywołuje- output.writeInt(recording.length);
my recordingEnded() w bloku finally. output.write(recording);
Tak wygląda przesłanie nagrania, teraz zo- output.flush();
baczmy, co dzieje się po drugiej stronie druta WalkieTalkie.getMainForm().setStatus("Recording sent");
(na szczęście wirtualnego). Metodę odbiera- } catch (IOException e) {
jącą dane zawiera Listing 15. Log.message("WalkieTalkieProcessor.outputUpdate(): " +
Jak widać, metoda ta będzie działać, do- e.getClass().getName() + ": " + e.getMessage());
póki zmienna process będzie miała war- process = false;
tość false. Zauważmy jednak, że po ewen- } catch (Exception e) {
tualnym wyjściu z bloku while, nie wywołu- Log.message("WalkieTalkieProcessor.outputUpdate(): " +
jemy metody closeConnection(), a to dla- e.getClass().getName() + ": " + e.getMessage());
tego, że zostanie to już wykonane w meto- } finally {
dzie outputUpdate(). Pierwsza rzecz, jaką recordingEnded();
wykonamy w bloku while, to próba odczy- }
tania długości nagrania metodą readInt(). } else {
Jest to metoda blokująca, więc nasz wątek try {
będzie zablokowany, dopóki nie otrzyma- Thread.sleep(100);
my nagrania od drugiego urządzenia. W } catch (InterruptedException ex) {
przypadku gdy połączenie zostanie prze- }
rwane przez drugą stronę, metoda ta rzuci }
wyjątek IOException. W bloku obsługują- }
cym ten wyjątek ustawiamy wartość zmien- closeConnection();
nej process na false, dzięki czemu może- }
my być pewni, że nasze wątki zostaną za-
kończone i będziemy mogli nawiązać no- (...)
we połączenie. Załóżmy, że druga strona }
przesyła nam nagranie i metoda readInt()

www.sdjournal.org 57
Programowanie urządzeń mobilnych

zwraca jego wielkość. Mając te informa- rane nagranie, co też czynimy. Teraz może- już to się stanie, tworzymy obiekt klasy
cje, możemy zaalokować pamięć na odbie- my spokojnie odczytać całe nagranie, gdy ByteArrayInputStream, którym posłużymy
się przy odtwarzaniu dźwięku.
Listing 15. Fragment klasy WalkieTalkieProcessor Przechodzimy do odtworzenia dźwięku,
zanim jednak do tego przystąpimy, musimy
public class WalkieTalkieProcessor { upewnić się, czy przypadkiem w tej samej
chwili nie nagrywamy dźwięku. W tym celu
private static boolean playingSound = false; wywołujemy metodę waitForRecording().
Gdy opuścimy tę metodę, możemy zacząć
private static void inputUpdate() { odtwarzać nasz dźwięk, używając metody
while (process) { startPlayback() klasy SoundPlayer. W ra-
try { zie jakiegokolwiek wyjątku w tym momen-
final int recordingLen = input.readInt(); cie informujemy oczekujący wątek, że od-
twarzanie zakończyło się (choć w zasadzie
WalkieTalkie.getMainForm().setStatus("Recieving recording"); jeszcze się nie zaczęło) w bloku catch, uży-
byte[] soundRecieved = new byte[recordingLen]; wając metody playbackEnded(). Jeśli tak się
input.readFully(soundRecieved); stanie, kontynuujemy działanie pętli while
od początku. Załóżmy, że wszystko zadziała-
ByteArrayInputStream soundInput = new ByteArrayInputStream(soundRe ło prawidłowo i nasz dźwięk jest odtwarza-
cieved); ny. O tym, czy nagrywanie zakończyło się,
try { możemy dowiedzieć się, używając metody
waitForRecording(); isPlaying() klasy SoundPlayer. Posłuży-
playingSound = true; my się nią, aby aktualizować nasz przepięk-
WalkieTalkie.getMainForm().setStatus("Playing recording"); ny pasek postępu.
SoundPlayer.startPlayback(soundInput); Kiedy odtwarzanie zakończy się i opuści-
} catch (Exception e) { my pętle while aktualizującą pasek postępu,
Log.message("WalkieTalkieProcessor.outputUpdate(): " + jedyne, co nam pozostaje, to poinformować
e.getClass().getName() + ": " + e.getMessage()); o zakończeniu metodą playbackEnded().
playbackEnded(); Nasz wątek odbierający dźwięk będzie da-
continue; lej oczekiwał na dane nadchodzące z dru-
} giej strony.

int value = 0; Podsumowanie


WalkieTalkie.getMainForm().setTimerValue(value); I tak oto zaprzęgliśmy do pracy dwie biblio-
while (SoundPlayer.isPlaying()) { teki, teoretycznie ze sobą niezwiązane. Za-
value++; pewne istnieje dużo potencjalnych zastoso-
WalkieTalkie.getMainForm().setTimerValue(value); wań połączeń Bluetooth. Przykładowo, ma-
try { jąc kod, który właśnie napisaliśmy, może-
Thread.sleep(100); my łatwo stworzyć prostą grę multiplayer.
} catch (InterruptedException ex) { Równie dobrze możemy stworzyć np. tek-
} stowy czat, który po kilku modyfikacjach
} zezwalałby na większą ilość urządzeń w na-
WalkieTalkie.getMainForm().setTimerValue(WalkieTalkie.RECORDING_ szej sieci piconet. Mam nadzieję, że zachę-
LENGTH * 10); ci to wszystkich do własnych eksperymen-
playbackEnded(); tów, pewnie wielu z czytelników ma w kie-
WalkieTalkie.getMainForm().setStatus("Playback ended"); szeni telefon komórkowy z Javą wspierają-
} catch (IOException e) { cy odpowiedni JSR, być może nawet o tym
Log.message("WalkieTalkieProcessor.inputUpdate(): IOException: " + nie wiedząc. Ogranicza nas tylko nasza wy-
e.getMessage()); obraźnia, zachęcam więc do eksperymen-
process = false; tów oraz do dzielenia się nimi z innymi
} (np. ze mną).
}
}
(...)
}
SZYMON ULEWICZ
Na co dzień zajmuje się pisaniem aplikacji oraz
gier J2ME, choć nie stroni od innych technologii,
W Sieci niekoniecznie mobilnych. W wolnym czasie, któ-
rego nie ma zbyt wiele, oprócz nabywania no-
• http://java.sun.com/javame/reference/apis/jsr118/ – dokumentacja MIDP 2.0;
wych programistycznych umiejętności, lubi od-
• http://java.sun.com/javame/reference/apis/jsr135/ – dokumentacja MMAPI;
• http://java.sun.com/javame/reference/apis/jsr082/ – dokumentacja Bluetooth API. prężyć się przy swojej konsoli.
Kontakt z autorem: szymon.ulewicz@gmail.com

58 12/2009
Warsztaty

Czyń CUDA (część 1)


Architektura
GPGPU to skrót, który na ustach informatyków pojawia się coraz częściej.
Oznacza general-purpose computing on graphics processing units,
czyli możliwość przeprowadzania dowolnych silnie zrównoleglonych
obliczeń na procesorach kart graficznych, których spora moc była do tej
pory wykorzystywana jedynie do generowania grafiki trójwymiarowej,
czyli w wielu przypadkach okazjonalnie.
CUDA. Zapowiada ona rewolucję, umożliwia-
Dowiesz się: Powinieneś wiedzieć: jąc wykonywanie zupełnie dowolnych obliczeń
• Z artykułu dowiesz się, jak wykorzystać dodat- • Od czytelnika wymagana jest znajomość na GPU dzięki modelowi programowania, który
kową moc obliczeniową oferowaną przez kar- C++ oraz podstawowych zasad programo- nie wymaga biegłej znajomości grafiki kompute-
ty graficzne, zarówno te wyspecjalizowane, jak wania równoległego. rowej. Korzystając z CUDA, możemy przyspie-
i te „zwykłe”, we własnych programach. szyć niektóre fragmenty programu wielo-, a w
skrajnych przypadkach nawet kilkusetkrotnie
(zob. ostatni link w Ramce Więcej w Sieci). Aby
zwykłe karty, np. z serii NVIDIA G8x (GeFor- uzyskać taki wynik, zadanie musi jednak chętnie
ce i Quadro). Oczywiście idea użycia proceso- poddawać się zrównolegleniu, przy czym najle-
Poziom rów GPU do obliczeń nie związanych z grafi- piej, by wydajność algorytmu była ograniczona
trudności ką 3D (stosowany w tym kontekście angielski przez złożone obliczenia, a nie przez wymaga-
skrót GPGPU oznacza general-purpose compu- ną wielkość pamięci. Jednak nawet jeżeli tak nie
ting on graphics processing units) nie pojawiła się jest, nie stoimy na straconej pozycji. Zwykle uda-
zupełnie znikąd. Już od pewnego czasu możli- je się uzyskać dziesięciokrotne przyspieszenie, a

W
iększość nowych komputerów we jest modyfikowanie działania kart graficz- dzięki stosowaniu optymalizacji współczynnik
wyposażonych jest w kilka pro- nych. Polega to na przygotowaniu niewielkich ten można dodatkowo zwiększać. Przykładowe
cesorów lub w procesory wie- programów (shaderów), które zastępowały nie- aplikacje wykorzystujące CUDA są dostępne na
lordzeniowe. Ten fakt wykorzystują systemy które elementy potoku przetwarzania grafiki. oficjalnej stronie NVIDIA (zob. dwa ostatnie ad-
operacyjne, planując wykonywanie procesów W szczególności dotyczy to niestandardowego resy w Ramce Więcej w Sieci).
i ich wątków. Mogą go również wykorzystać przetwarzania werteksów i pikseli, co pozwala W poniższym artykule zapoznamy się m.in.
programiści do przyspieszenia działania apli- na stosowanie ciekawych efektów znacznie wy- z architekturą współczesnych kart graficznych.
kacji w czym pomogają takie technologie, jak dajniejszych lub w ogóle trudnych do uzyskania Wyewoluowała ona do obecnej postaci dzięki
OpenMP i Intel Threading Building Blocks. z poziomu kodu programu korzystającego tylko ogromnemu zapotrzebowaniu na realistyczne
Oprócz procesorów CPU, których te dwie z OpenGL lub DirectX/XNA. Pierwsze tego ty- efekty graficzne w grach komputerowych. Spe-
wybrane technologie dotyczą, jest jednak w pu programy przygotowywane były w asemble- cyfika takich obliczeń wymaga identycznych
większości nowych komputerów „moc prze- rze, ale szybko w NVIDII powstał język Cg (od operacji na pokaźnych ilościach danych. I do
robowa”, która w większości aplikacji jest ang. C for graphics), a potem GLSL (OpenGL) i takich równoległych obliczeń ze wspólnym al-
niezagospodarowana. Mam na myśli proceso- HLSL (DirectX). Wszystkie one, dzięki wzoro- gorytmem są one przystosowane. Lektura tego
ry zainstalowane na kartach graficznych. waniu na języku C, umożliwiły programowanie artykułu pozwoli także opanować podstawy in-
Ich wykorzystanie do dowolnych celów pla- kart graficznych szerszej grupie programistów terfejsu programistycznego (API) CUDA oraz
nują umożliwić obaj wielcy gracze na rynku przyzwyczajonych do języków wyższego po- umożliwi napisanie pierwszych programów ko-
kart graficznych, tj. NVIDIA i ATI/AMD. NVI- ziomu. Wykorzystanie ogromnych możliwości rzystających z tej technologii. W kolejnym arty-
DIA przygotowała w tym celu technologię CU- zrównoleglenia obliczeń na GPU nie mogło nie kule skupimy się natomiast na podstawowych
DA (z ang. Compute Unified Device Architec- doczekać się prób stosowania tych rozwiązań do technikach optymalizacji kodu korzystającego
ture), a konkurent ATI Stream. Obie umożli- zagadnień nie związanych bezpośrednio z gra- z CUDA. Początkowo mogą się wydać one dość
wiają wykorzystanie mocy obliczeniowej pro- fiką komputerową. Ciekawą próbą jest PhysX, trudne, jednak z czasem stają się naturalne. Aby
cesorów montowanych na współczesnych kar- technologia firmy Ageia, wykupiona przez NVI- testować przedstawione niżej programy, wska-
tach graficznych (GPU) oraz ich pamięci. Cho- DIA, która pozwala wykorzystać karty graficzne zane jest posiadanie karty graficznej firmy NVI-
dzi zarówno o specjalne karty dedykowane do do symulacji fizycznych w grach: od wybuchów DIA wspierającej CUDA. Nie jest to jednak wa-
obliczeń naukowo-inżynierskich, w których poprzez interakcje przedmiotów, cieczy, po sy- runek konieczny. Programy można bowiem
nie ma w ogóle wyjścia wideo (w szczególno- mulacje tkanin. Nie może jednak równać się ze uruchamiać także w trybie emulacji. Wiąże się
ści układy NVIDII z procesorem Tesla), jak i o swobodą, jaką programistom daje technologia to jednak z pewnymi ograniczeniami.

60 12/2009
Technologia NVIDIA CUDA

Należy również zastrzec, że nie tylko NVI- 2.2 (na stronie dostępne są również pakiety in- tor NVCC, niezbędne biblioteki, pliki na-
DIA i ATI zajmują się obecnie zagadnieniami stalacyjne dla popularnych dystrybucji Linuxa). główkowe oraz dokumentację.
GPGPU. Także inni producenci procesorów W tym celu musimy pobrać i zainstalować: • Ostatni element jest opcjonalny, niemniej
coraz uważniej przyglądają się rynkowi zwią- jednak wart rozważenia. Moim zdaniem
zanemu z obliczeniami prowadzonymi na kar- • Najnowszy sterownik firmy NVIDIA do- Czytelnik powinien zainstalować także
tach graficznych. Przykładem jest Intel, któ- stępny dla naszej karty graficznej zawie- pakiet narzędzi programistycznych CU-
ry niedawno zapowiedział procesor Larrabee. rający sterownik CUDA (CUDA Driver). DA (CUDA SDK).
W przeciwieństwie do procesorów ATI i NVI- Możemy go pobrać ze strony CUDA lub Instalacji najlepiej dokonać w podfolderze
DIA, Larrabee powstał na bazie procesora CPU, bezpośrednio z witryny NVIDIA. Przed sdk katalogu, w którym zainstalowane są
a konkretnie popularnego Pentium, który zo- instalacją nowego sterownika musimy sterowniki CUDA. W skład tego zestawu
stał przystosowany do wykonywania obliczeń pamiętać o usunięciu obecnego (może- narzędzi wchodzą m.in. liczne przykłady
na potrzeby grafiki 3D. Aktywność w tej dzie- my tego dokonać w aplecie Dodaj lub usuń użycia CUDA (w celu weryfikacji popraw-
dzinie przejawia również Microsoft, który zapo- programy z Panelu sterowania); Po tym za- ności instalacji wskazane jest uruchomienie
wiada interfejs programistyczny DirectCompu- biegu, podobnie jak i po instalacji nowego kilku skompilowanych przykładów znaj-
te (jako składnik DirectX będzie obecny w każ- sterownika, będziemy zmuszeni do po- dujących się w folderze CUDA\sdk\bin\
dej edycji Windows 7). Podobnie jak technolo- nownego uruchomienia komputera. win32\Release) oraz dodatkowe narzędzia.
gie ATI i NVIDII także ona będzie umożliwia- • Pakiet narzędziowy CUDA (CUDA To-
ła wykorzystanie procesorów karty graficznej olkit). W podkatalogu CUDA SDK\projects\template
do dowolnych obliczeń. Jednak dzięki temu, W trakcie instalacji można zmienić do- znajduje się szablon projektu przygotowany
że jej obecność będzie gwarantowana na wszyst- myślny folder instalacji pakietu. Ponie- przez NVIDIA. Niestety wymaga on, żebyśmy
kich nowych komputerach zawierających Di- waż z jego programów będziemy korzy- tworzone na jego podstawie własne projekty
rectX 11, masowo korzystać z niej będą zapew- stać z linii komend, warto zadbać, aby umieszczali w folderze z przykładami zawar-
ne twórcy gier komputerowych. Alternatywą ścieżka katalogu nie była zbyt długa. W tymi w SDK. Przeniesienie projektu do inne-
jest OpenCL (ang. Open Computing Language) poniższych przykładach zakładamy, że go katalogu powoduje błąd kompilacji, ponie-
rozwijane przez konsorcjum Khronos Group, pakiet zainstalowany jest w podkatalogu waż szablon szuka potrzebnych bibliotek, ko-
zajmujące się m.in. nadzorowaniem biblioteki toolkit katalogu sterowników CUDA. Pa- rzystając ze ścieżek względnych. Możemy ten
OpenGL. Podejrzewamy jednak, że dołączenie kiet narzędziowy zawiera m.in. kompila- problem rozwiązać na kilka sposobów. Jedną z
DirectCompute do systemu operacyjnego, po-
dobnie jak było w przypadku pojedynków Di-
rect3D vs. OpenGL, ADO vs. BDE i Internet
Explorer vs. Netscape Nawigator, może dać tej
technologii zdecydowaną przewagę w konku-
rencji z OpenCL. Należy jednak wyraźnie za-
znaczyć, że nie jest to konkurencja dla rozwią-
zań sprzętowych, a jedynie pomiędzy różnymi
metodami ich programowania.

Instalacja
Niezbędne pakiety instalacyjne możemy pobrać
z oficjalnej strony projektu (zob. ramka Więcej w Rysunek 2. Zmiennopozycyjna moc obliczeniowa oraz przepustowość pomiędzy kartą graficzną a
Sieci). W dziale Downloads możemy wybrać in- procesorem (ten i kolejne rysunki pochodzą z materiałów firmy NVIDIA udostępnionych na stronie nvidia.com)
teresującą nas platformę sprzętową. Opiszę in-
stalację w 32-bitowym systemie Windows XP
najnowszej publicznie dostępnej wersji CUDA

Rysunek 1. Ustawienia zmiennych


środowiskowych na potrzeby CUDA Rysunek 3. Architektura CUDA

www.sdjournal.org 61
Warsztaty

możliwości jest dodanie do ustawień projektu ki temu nie jest potrzebny wyszukany system • Sterownik w trybie użytkownika, który
bezwzględnej lokalizacji katalogu CUDA\sdk\ kontroli przepływu instrukcji. Takie podejście dostarcza niskopoziomowe API dla pro-
common\inc. Druga opcja, z której tu skorzysta- pozwala również na rezygnację z rozbudowa- gramistów.
my, to utworzenie zmiennej środowiskowej za- nych buforów, gdyż opóźnienia w dostępie do • Niskopoziomowa maszyna wirtualna
wierającej tę ścieżkę. Ponadto będziemy korzy- pamięci można maskować, wykonując w tym PTX (z ang. parallel thread execution) oraz
stać ze zmodyfikowanego projektu umieszczo- czasie operacje w innym wątku. zbiór instrukcji asemblera (ISA). O ma-
nego na CD dołączonego do tego numeru SDJ. Technologia CUDA została przedstawio- szynie PTX zobacz w dokumentacji do-
W tym celu dodajemy zmienną środowiskową na przez firmę NVIDIA po raz pierwszy łączonej do sterowników (plik CUDA\
o nazwie NVSDKCUDA _ ROOT wskazującą na ka- w listopadzie 2006 roku. Obejmuje ona za- toolkit\doc ptx_isa_1.4.pdf).
talog zawierający pakiet CUDA SDK. Żeby te- równo nowy model programowania równole-
go dokonać, musimy kliknąć prawym przyci- głego wraz z zestawem instrukcji niskopozio- Najnowsza karta graficzna firmy NVIDIA
skiem myszy na Mój komputer, wybrać Wła- mowych dla karty graficznej, jak i język pro- jest zbudowana z multiprocesorów strumie-
ściwości, następnie w zakładce Zaawansowane gramowania wysokiego poziomu integrujący niowych (SM). Karta z Rysunku 4 składa się
klikamy na Zmienne środowiskowe. Klikamy na się z językiem C. Jak widać na Rysunku 3, in- z 30 multiprocesorów. Schematyczną budo-
Nowa i wprowadzamy nazwę nowej zmiennej ne języki będą również wspierane w niedale- wę multiprocesora można z kolei zobaczyć na
oraz jej wartość (Rysunek 1). kiej przyszłości. Rysunku 5. Każdy SM składa się z:
Programiści przyzwyczajeni do środowiska NVIDIA deklaruje wsparcie dla wszyst-
Microsoft Visual C++ nie muszą z niego rezy- kich języków i interfejsów programistycz- • I-Cache – bufor instrukcji;
gnować. Do integracji CUDA z tym środowi- nych (API), które umożliwiają programi- • MT Issue – rozdziela zadania dla SP
skiem służy program CUDA VS2005 Wizard. stom korzystanie z mocy obliczeniowej kart i SFU (skróty wyjaśnione w następnych
Dzięki niemu w środowisku pojawi się nowy graficznych. Dlatego oprócz opracowanego punktach);
typ projektu wspierającego opisywaną techno- przez NVIDIA języka C for CUDA można • C-Cache – 8KB bufor stałych, który
logię o nazwie CUDA WinApp. używać także innych modeli programowa- przyspiesza odczyt z obszaru pamięci sta-
nia. W niniejszym dodatku przedstawione łej (ang. constant memory);
Architektura są jednak informacje dotyczące tylko tego naj- • 8 x SP – 8 jednostek obliczeniowych (na-
Dzięki ogromnemu zapotrzebowaniu na wy- bardziej obecnie dopracowanego i popular- zywanych w tradycyjnych kartach gra-
sokiej jakości efekty w grach komputerowych nego rozwiązania. Pozostałe przedstawione ficznych stream processors, stąd skrót),
rozwój kart graficznych postępował błyska- na Rysunku 3, a mianowicie DirectCompu- które wykonują większość obliczeń po-
wicznie (Rysunek 2) Obecnie są one wysoce te oraz OpenCL, są dostępne w chwili pisa- jedynczej precyzji (każdy zawiera własne
równoległymi, wielowątkowymi, wieloproce- nia tego artykułu tylko dla grupy wybranych 32-bitowe rejestry);
sorowymi kartami o wielkiej mocy obliczenio- osób, choć, jak zaznaczyłem we wstępie, po- • 2 x SFU (ang. special function units, jed-
wej i wysokiej przepustowości pamięci. pularność szczególnie rozwiązania Microsoft nostki specjalne). Ich zadania to m.in. ob-
Widoczna na wykresach z Rysunku 2 róż- może nas niebawem zaskoczyć. Różnice mię- liczanie funkcji przestępnych, np. trygo-
nica teoretycznej wydajności między CPU dzy tymi modelami nie są zresztą zbyt duże. nometrycznych, wykładniczych i logaryt-
a GPU wynika z tego, iż GPU specjalizuje się Z pewnością opanowanie modelu NVIDIA C micznych, czy interpolacja parametrów.
w intensywnych obliczeniowo, wysoce równo- for CUDA ułatwi ewentualną migrację do in- Sprzętowo zaprogramowane funkcje są
ległych obliczeniach, czyli dokładnie w takich nego rozwiązania. Najbardziej podobne do obliczane w 16 cyklach. Pozostałe funkcje
obliczeniach, jakie są realizowane w ramach naszego środowiska jest OpenCL, z którego potrzebują co najmniej 32 cykli (np. pier-
potoku przetwarzania grafiki trójwymiarowej. specyfikacją można się już zapoznać. Różnice wiastek lub funkcja wykładnicza);
Procesory graficzne składają się z większej ilo- między nim a przedstawianym w tym dodat-
ści tranzystorów przeznaczonych do przetwa- ku C for CUDA można nazwać kosmetyczny-
rzania danych kosztem jednostek odpowie- mi. Na podstawie prezentacji przedstawionej
dzialnych za buforowanie danych oraz kontro- przez przedstawicieli NVIDIA można mieć
lę przepływu instrukcji. Ściślej mówiąc, kar- również nadzieję, że podobnie będzie w przy-
ta graficzna jest przeznaczona do wykonywa- padku DirectCompute.
nia zadań, które można określić jako zbliżo- W skład architektury CUDA wchodzi:
ne do architektury SIMD (ang. Single Instruc-
tion, Multiple Data – jedna instrukcja, wiele • Równoległy silnik obliczeniowy umiesz-
danych) znanej z komputerów wektorowych. czony na karcie graficznej.
Oznacza to, że ten sam program uruchamia- • Wsparcie na poziomie jądra systemu dla
ny jest równolegle dla różnych danych. Dzię- zainicjowania sprzętu, konfiguracji itp.

Rysunek 4. Architektura kart serii 10 Rysunek 5. Multiprocesor strumieniowy

62 12/2009
Technologia NVIDIA CUDA

• DP – jeden procesor podwójnej precyzji; nie znajduje się w buforze. Wówczas dochodzi programy przygotowane dla obecnej wersji bę-
• SM (ang. shared memory) – pamięć współ- do odczytu z pamięci karty. dą współpracować z nowszymi wydaniami API.
dzielona (16KB). Mają do niej dostęp Gdy cały half-warp czyta ten sam element z Kombatybilność w przód nie jest zachowana
wszystkie wątki z danej kraty. bufora, to czas odczytu odpowiada dostępowi (programy napisane dla najnowszych wersji API
do rejestru. Koszt rośnie liniowo wraz z ilością mogą nie zadziałać na starszych wydaniach).
Multiprocesor tworzy, zarządza i wykonuje odczytywanych adresów. Pisząc programy w C for CUDA, należy my-
równolegle wątki praktycznie bez opóźnień śleć o docelowym urządzeniu, na jakim będzie
wywołanych przez planistę. Warto zwrócić Pamięć tekstur uruchamiany program. Każda z kart posia-
uwagę na to, że bariera potrzebuje tylko jed- Tylko do odczytu, buforowana. Koszt odczytu da pewną zdolność obliczeniową. Karty z se-
nej instrukcji procesora. Te cechy pozwala- rośnie, gdy potrzebna wartość nie znajduje się rii G80 należą do klasy 1.0, karty G84, G86,
ją znacznie rozdrabniać zadania. Można na w buforze. Wówczas dochodzi do odczytu z G92, G94, G96 i G98 do 1.1, a karty GT200
przykład przydzielać jeden wątek do każdego pamięci karty. Bufor jest zoptymalizowany do do 1.3 (nie powstały karty klasy 1.2). Tabela
piksela na obrazku. W celu zarządzania tak odczytywania bliskich adresów, tzn. osiąga naj- 1 zawiera zestawienie najważniejszych różnic
dużą ilością, często wykonujących różne za- lepsze rezultaty, gdy wątki z warpa chcą odczy- pomiędzy poszczególnymi klasami.
dania, wątków multiprocesory stosują nową tać adresy znajdujące się blisko siebie. Podczas konferencji GPU Technology Con-
architekturę zwaną SIMT (ang. single-instruc- ference w San Jose (USA), która odbyła się
tion, multiple-thread). Multiprocesor przydzie- Pamięć globalna ostatniego dnia września bieżącego roku
la każdy wątek do jednego procesora skalarne- Dostęp do niej trwa 400-600 cykli. Nie jest bu- (30.09.2009), została zaprezentowana no-
go. Każdy wątek ma własny zestaw instruk- forowana. wa architektura o nazwie Fermi (zob. http:
cji oraz stan rejestrów. Multiprocesor zarzą- Pierwsza publiczna implementacja NVIDIA //gadgets.softpedia.com/news/NVIDIA-Unve-
dza wątkami w grupach liczących po 32 wąt- CUDA została przedstawiona w lutym 2007 ils-New-Fermi-CUDA-GPU-Architecture-5440-
ki zwanych warpami. Nazwa wywodzi się (wersja beta 0.8). Kolejna główna wersja (2.0) zo- 01.html). Prawdopodobnie wraz z nią zosta-
z tkactwa, gdzie ang. warp oznacza osnowę. stała wydana wraz z premierą kart serii GT200 nie wydane CUDA 3.0. Jej największe nowo-
Definiuje się również half-warpa, który skła- (Tesla 10). Każda kolejna wersja jest kompatybil- ści to ośmiokrotne zwiększenie wydajności w
da się z pierwszej bądź drugiej połowy war- na wstecz. Możemy więc mieć nadzieję, że także obliczeniach o podwójnej precyzji w stosunku
pa. Pojedyncze wątki wewnątrz warpa star-
tują z tymi samymi instrukcjami, jednak każ-
������
dy wątek może się rozgałęzić i podążać własną
ścieżką instrukcji. Wskazane jest, aby wszyst-
����������������
kie wątki wewnątrz warpa wykonywały te sa-
me instrukcje. Gdy dochodzi do rozgałęzie-
nia, to wszystkie wątki z warpa muszą wyko-
nywać instrukcje z obu rozgałęzień, a część z
����������������
nich po prostu nie będzie miała żadnego efek-
tu. Każdy wątek ma dostęp do następujących
����������������
pamięci (Rysunek 6):

Rejestry procesora
Każdy multiprocesor posiada 8192 (wersja do �������������
1.1) lub 16384 (w wersjach od 1.2) 32-bito-
wych rejestrów. Zwykle dostęp do rejestru nastę-
��������� ��������� ���������
puje bez dodatkowych opóźnień, chyba że pró-
�����������
bujemy odczytać wartość z rejestru, do którego ����
dopiero co pisaliśmy. Aby uniknąć wynikających
����������� ����������� �����������
z tego opóźnień, należy przydzielić co najmniej
192 aktywne wątki na każdy multiprocesor. W
celu uniknięcia konfliktów należy ilość wątków
na blok ustawić jako wielokrotność liczby 64. ��������
�����
Pamięć lokalna
Dostęp do niej trwa 400-600 cykli. Nie jest
�������
buforowana. Odczyt z niej jest zawsze złączo- �����
ny (ang. coalesced), bo z definicji pamięć ta
jest zdefiniowana dla wątku. Przechowuje du-
że struktury, duże tablice, tablice, które nie są
indeksowane stałymi.
�������������

Pamięć współdzielona
Tak szybka jak rejestry, gdy nie ma konfliktów.

Pamięć stała
Jest to pamięć tylko do odczytu, buforowana.
Koszt odczytu rośnie, gdy potrzebna wartość Rysunek 6. Schemat pamięci

www.sdjournal.org 63
Warsztaty

do kart GT200, możliwość zastosowania pa- zyków programowania. W niniejszym arty- deklaracje. Wywołanie jądra z poziomu pro-
mięci ECC, pamięć podręczna, przyspiesze- kule jednak decydujące znaczenie miał fakt, gramu wygląda podobnie jak wywołanie zwy-
nie przełączania kontekstu oraz operacji ato- iż API niskopoziomowe nie może korzystać z kłej funkcji C, z tym że uzupełnione jest o do-
mowych. Nie wiadomo, kiedy nowe karty tra- trybu emulacji, co znacznie utrudnia napisa- datkowe parametry:
fią do sprzedaży, jednak z przecieków wynika, nie poprawnego programu początkującemu
że będzie to pierwszy kwartał 2010 roku. użytkownikowi CUDA. Siłą rzeczy uniemożli- kernel<<<dim3 grid, dim3 block,
wia także naukę osobom nieposiadającym naj- size_t Ns, cudaStream_t S>>>(parameter s)
Sygnatura jądra nowszych kart firmy NVIDIA.
Skupimy się na modelu używającym NVIDIA Na początek zdefiniujmy pojęcie jądra (ang. Parametry umieszczane wewnątrz nawiasów
C for CUDA wraz z wysokopoziomowym API kernel), czyli funkcji, która będzie uruchamia- <<< >>> są charakterystyczne dla C for CU-
(ang. runtime API). Niskopoziomowe API wy- na równolegle na karcie graficznej. Może być DA i mają następujące znaczenie:
maga więcej kodu, jest trudniejsze w używa- wywoływany tylko przez wątek działający na
niu i debugowaniu, ale oferuje większą kon- CPU. W kodzie można go rozpoznać po słowie • grid – ustala wymiar i rozmiar kraty. Ilo-
trolę i umożliwia wykorzystywanie innych ję- kluczowym __global__ poprzedzającym jego czyn grid.x*grid.y odpowiada suma-
rycznej liczbie bloków, które zamierzamy
Listing 1. Instrukcje inicjujące kartę graficzną. Dla ułatwienia orientacji w istniejącym już kodzie uruchomić. Ponieważ parametr grid jest
projektu startowego w listingach umieściłem numery linii typu dim3, a więc zawiera trzy wymiary,
to grid.z jest domyślnie ustawiany na 1.
14 // wybiera GPU podane z linii komend, • block – ustala wymiar i rozmiar każdego
15 // badz te o najwiekszej ilosci Gflops / s bloku. Iloczyn współrzędnych tego wek-
16 if( cutCheckCmdLineFlag( argc, (const char **)argv , "device") ) tora odpowiada liczbie wątków wewnątrz
17 cutilDeviceInit(argc, argv); każdej kraty.
18 else • Ns – jest opcjonalnym argumentem od-
19 cudaSetDevice( cutGetMaxGflopsDeviceId() ); } powiadającym za ilość dynamicznie przy-
dzielonej pamięci współdzielonej dla każ-
Listing 2. Parametry zależne od ilości wątków dego z bloków. Domyślnie wynosi 0.
21 int num threads = 16 ; // podzielne przez 4 • S – również jest opcjonalnym argumen-
22 int mem size = sizeof(int) * num threads; tem o domyślnej wartości równej 0. Zrozu-
mienie znaczenia tego parametru wymaga
Listing 3. Zmienne przechowywane w pamięci komputera i w pamięci karty graficznej znajomości pewnych faktów nie omówio-
24 int* h_data = (int*) malloc(mem_size); nych dotychczas; tymczasem wspomnę
25 int* d_data ; tylko, że parametr ten dotyczy strumienia.
26 cutilSafeCall(cudaMalloc( (void**) &d_data, mem_size));
Poza tymi specjalnymi parametrami do jądra
Listing 4. Uruchomienie jądra przekazywany jest również zwykły argument
28 // ustawia rozmiar kraty i bloku typu parameters. Jest to ciąg parametrów, któ-
29 dim3 grid ( num_threads/4, 1, 1); rego długość jest ograniczona do 512 bajtów.
30 dim3 threads(4, 1, 1); Każdy wątek uruchamiający jądro (kernel)
31 posiada unikalny identyfikator. Dostęp do te-
32 // uruchom kernel go numeru z poziomu jądra można uzyskać
33 zad1Kernel<<< grid, threads >>>( d_data ); dzięki wbudowanej zmiennej threadIdx, któ-
ra jest typu dim3, dzięki czemu możemy bez
Listing 5. Synchronizacja wątków CUDA i kopiowanie wyników do pamięci komputera trudu ustalić pozycje wątku w kracie.
35 // sprawdz , czy kernel zakonczyl sie sukcesem Funkcje jądra muszą respektować kilka ogra-
36 cutilCheckMsg("Kernel execution failed"); niczeń: zawsze zwracają tym void, mają dostęp
37 tylko do pamięci karty graficznej, muszą mieć
38 // kopiuje wynik z GPU do pamieci komputera stałą ilość argumentów, nie mogą posiadać wy-
39 cutilSafeCall(cudaMemcpy(h_data, d_data, mem_size, wołań rekurencyjnych i nie posiadają zmien-
40 cudaMemcpyDeviceToHost) ); nych statycznych. Ponadto należy pamiętać, że
wywołanie jądra jest asynchroniczne, co ozna-
cza, że program, który go wywołuje, natych-
miast odzyskuje kontrolę (prawdopodobnie
Tabela 1. Zdolności obliczeniowe kart graficznych przed zakończeniem działania funkcji jądra).
Zdolność obliczeniowa 1.0 1.1 1.2 1.3 Aby lepiej zrozumieć numeracje wątków,
Funkcje atomowe w pamięci globalnej - • • • bloków i krat, przyjrzyjmy się bliżej Rysunkowi
7. Widać na nim, że jądro o nazwie Kernel 1 zo-
Funkcje atomowe w pamięci współdzielonej - - • • stało uruchomione dla kraty o rozmiarze 3 x 2,
Ilość rejestrów na multiprocesor 8192 8192 16384 16384 w której każdy blok ma rozmiar równy 5 x 3 x 1.
Maksymalna liczba warpów na multiprocesor 24 24 32 32 Na czerwonym tle widzimy identyfikator wąt-
ku, który możemy odczytać za pomocą zmien-
Maksymalna liczba aktywnych wątków na multiproce- 768 768 1024 1024
sor nej threadIdx, a na pomarańczowym wartość
zmiennej blockIdx, czyli identyfikator obec-
Podwójna precyzja - - - • nego bloku. Pośród zmiennych, do których wą-

64 12/2009
Technologia NVIDIA CUDA

tek ma dostęp z poziomu jądra, ważne są jesz- litego bloku zarezerwowanej pamięci. W ra- cudaSuccess i jest nadpisywana za każdym
cze blockDim i gridDim, które przechowują od- zie sukcesu funkcja zwraca wartość równą razem, gdy pojawi się błąd. Aby go odczytać,
powiednio wymiar bloku i kraty. Kolejna waż- stałej cudaSuccess. Należy zwrócić uwagę na a jednocześnie przywrócić jej domyślną war-
na zmienna to warpSize, jednak jest ona używa- to, że funkcja cudaMalloc (jak i wiele innych) tość, należy użyć funkcji cudaGetLastError.
na zdecydowanie rzadziej, niż wyżej wymienio- może zwracać błędy pochodzące z uprzednio Jeżeli chcemy być pewni, że działanie funk-
ne zmienne. Jej znaczenie zostanie wyjaśnione wywoływanych funkcji asynchronicznych. cji asynchronicznej zakończyło się sukce-
w następnym artykule. Błędy funkcji wykonywanych asynchronicz- sem, konieczne jest ustawienie za nią bariery
nie nie mogą być zwracane przez nie same – za pomocą funkcji cudaThreadSynchronize
Uruchamianie jądra i korzystanie główny wątek nie czeka na ich zakończenie i i sprawdzenie, jaką wartość zwróci ta funkcja.
z pamięci karty graficznej tym samym nie sczytuje zwracanych przez Gdy tego nie zrobimy, to błąd zostanie zgło-
Pierwsze zadanie, jakie sobie postawimy, ucząc nie wartości. W zamian program przecho- szony przez pierwszą funkcję, która wywołu-
się korzystania z CUDA, polega na urucho- wuje pewną zmienną zawierającą kod ostat- je synchronizację. Należy unikać wstawiania
mieniu bardzo prostego programu korzystają- niego błędu. Jest ona inicjowana wartością barier po każdym wywołaniu asynchronicz-
cego z architektury CUDA. W tym celu należy
otworzyć dostępny w materiałach umieszczo-
nych na CD dołączonym do tego numeru SDJ ���� ������
projekt startowy dla tego zadania. Zgodnie z
zapowiedzią będziemy korzystać z wysokopo-
������
ziomowego API zawartego w bibliotekach CU-
DA. Pierwszą czynnością, jaką musimy wyko-
nać, jest zainicjowanie karty graficznej. Na po- ��������
����� ����� �����
czątku funkcji main (plik zad1.cu) musimy za- ������ ������ ������
tem dopisać kod widoczny na Listingu 1.
Linia numer 16 korzysta z funkcji pomocni-
czej zawartej w pakiecie programistycznym do- ����� ����� �����
starczonym wraz z CUDA i pozwala z linii ko- ������ ������ ������
mend wybrać urządzenie, na którym chcemy do-
konywać obliczeń. Urządzenie wskazywane jest
za pomocą argumentu linii komend, a gdy uru-
chomimy nasz program bez parametrów, to do- ������
myślnie zostanie wybrana ta karta, której estyma-
tor ilości GFLOPS (zob. Ramka) jest największy.
��������
Korzystanie z funkcji pomocniczych zaczy-
nających się na cut.. lub cutil.. jest general-
nie niewskazane, gdyż nie ma gwarancji ich
obecności w różnych edycjach CUDA. Pełnią
one tylko funkcje pomocniczą. Później zapre- ������������
zentuję, co tak naprawdę robią i kiedy można
z nich zrezygnować. NVIDIA udostępnia ich
kod źródłowy, ale bez dokumentacji.
������ ������ ������ ������ ������
Ponieważ dopiero zapoznajemy się z tech- ������ ������ ������ ������ ������
nologią CUDA, użyta w pierwszym przykła-
dzie liczba wątków jest niewielka (Listing 2).
W programach przygotowywanych do praw- ������ ������ ������ ������ ������
������ ������ ������ ������ ������
dziwych obliczeń liczba wątków nigdy nie po-
winna być aż tak niska.
Następnie definiujemy dwa wskaźniki o na- ������ ������ ������ ������ ������
zwach h_data i d_data (Listing 3). Pierwszy (z ������ ������ ������ ������ ������
prefiksem h_) będzie służył do przechowywa-
nia wyników programu w pamięci komputera,
natomiast drugi (ten z prefiksem d_) będzie wy-
korzystywany przez kartę graficzną. Dla h_data Rysunek 7. Rozmieszczenie wątków na kracie
pamięć przydzielamy klasycznie, identycznie
jak to miało miejsce w języku C. Natomiast w
przypadku d_data skorzystamy z jednej z funk-
GFLOPS
Wartość GFLOPS (ang. FLoating point Operations Per Second – liczba operacji zmiennoprze-
cji służących do alokacji danych na karcie gra- cinkowych na sekundę) oblicza się, mnożąc ilość multiprocesorów, ilość procesorów na mul-
ficznej. Jej sygnatura jest następująca: tiprocesorze z częstotliwością taktowania karty. Otrzymany wynik należy pomnożyć przez
dwa, gdy mamy do czynienia z kartami w wersji 1.0 lub 1.1, gdyż mogą one wykonywać ope-
cudaError_t cudaMalloc( void** devPtr, racje FMAD (mnożenie i dodawanie: a*b+c) w jednym cyklu. Przez pewien czas twierdzono,
że procesor skalarny potrafi wykonać w jednej instrukcji oprócz FMAD również operacje MUL,
size_t count )
jednak w wyniku do końca niewyjaśnionego błędu okazało się to niemożliwe. Dopiero karty
z serii GT200 (model obliczeniowy 1.3) są w stanie wykonywać takie operacje. Z tego powo-
W *devPtr jest zwracany wskaźnik do zmien- du ich teoretyczną moc obliczeniową należy przemnażać nie przez 2, a przez 3.
nej count przechowującej ilości bajtów jedno-

www.sdjournal.org 65
Warsztaty

nym, gdyż może to znacznie obniżyć wydaj- Jest to dobra okazja, żeby zwrócić uwagę to w wyniku otrzymamy 16 przypadkowych
ność całego programu. Czytelnika na zachowanie funkcji pomocni- liczb całkowitych. Jest tak, ponieważ jądro
Teraz dochodzimy do najciekawszej części te- czych. Zdarza się, że działają one trochę inaczej jeszcze nic nie robi (ciało funkcji zad1Kernel
go programu, czyli uruchomienia jądra. Włączy- w trybie release, a inaczej w trybie debug. Po- z pliku zad1_kernel.cu jest puste). Zanim to
my go w blokach zawierających po 4 wątki. Ja- wyższa funkcja w trybie release wcale nie wy- poprawimy, wyczyśćmy obszar pamięci w kar-
ko parametr otrzyma wskaźnik na pamięć za- wołuje cudaThreadSynchronize. Po wykona- cie graficznej wskazywany przez d_data. Sko-
rezerwowaną w zmiennej d_data (przecho- niu programu na karcie graficznej i upewnie- rzystamy do tego z funkcji cudaMemset, która
wywanej w pamięci karty graficznej). Pokazu- niu się, że nie powstały żadne błędy, musimy ustawi każdy bajt wskazanego obszaru pamię-
je to Listing 4. Po wywołaniu funkcji jądra ste- jeszcze skopiować wyniki z powrotem do pamię- ci karty graficznej na zadaną wartość:
rowanie natychmiast wraca do głównego pro- ci komputera. Do tego celu skorzystamy z funk-
gramu. Aby poczekać na zakończenie działa- cji cudaMemcpy. Pokazuje to również Listing 5. 27 cutilSafeCall( cudaMemset( d_data, 0,
nia jądra, musimy uśpić program aż do odebra- Funkcja cudaMemcpy ma następującą składnię: mem_size ));
nia odpowiedniego komunikatu. Załatwi to za
nas funkcja pomocnicza cutilCheckMsg (Li- cudaError_t cudaMemcpy(void* dst, Dzięki temu po uruchomieniu programu po-
sting 5). Funkcja ta jest w zasadzie opakowa- const void* src, winniśmy na ekranie zobaczyć zbiór szesna-
niem do innej funkcji dostępnej w interfejsie size_t count, stu zer.
programistycznym CUDA, a mianowicie do enum cudaMemcpyKind
cudaThreadSynchronize1. kind) Programowanie jądra
Przejdźmy teraz do pliku zad1_kernel.cu za-
Listing 6. Zakończenie funkcji main Sądzę, że znaczenia parametrów dst , src i wierającego funkcję jądra uruchamianą na kar-
głównego programu count nie trzeba wyjaśniać. Ciekawy jest na- cie graficznej. Zaczniemy od wyznaczenia nu-
tomiast parametr kind . Może on przyjmować meru wątku, korzystając ze zmiennych bloc-
42 for(int i = 0; i < num_threads; ++i) jedną z trzech wartości: kIdx i threadIdx. Przy użytych przez nas ni-
43 printf("%d ", h_data[i]); skich wymiarach bloków i kraty zadanie to jest
44 • cudaMemcpyHostToDevice – kopiowanie stosunkowo łatwe . Następnie do wejściowej
45 //zwalnianie pamieci z pamięci komputera do pamięci GPU; tablicy o nowo obliczonym indeksie przypisz-
46 free( h_data ); • cudaMemcpyDeviceToHost – kopiowanie my jakąś wartość, na przykład 17 (Listing 7).
47 cutilSafeCall(cudaFree(d_data)); z pamięci GPU do pamięci komputera; Jest jasne, że gdy teraz uruchomimy program,
48 • cudaMemcpyDeviceToDevice – kopiowa- na ekranie pojawi się ciąg szesnastu liczb 17.
49 cudaThreadExit(); nie między dwoma obszarami pamięci na Jeżeli w tablicy zapiszemy wartość zmien-
50 cutilExit(argc, argv); karcie graficznej. nych tid (polecenie d_data[ tid ] = tid;),
threadIdx.x i blockIdx.x, uzyskamy wyniki
Listing 7. Wypełnianie pamięci wartościami 17 Pozostaje już tylko wyświetlić wynik obliczeń i równe odpowiednio:
5 const unsigned int tid = zwolnić zarezerwowaną pamięć. Pokazuje to Li-
blockIdx.x*blockDim.x + sting 6. Zwalniamy w nim także zasoby zajmo- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
threadIdx.x; wane przez nasz wątek po zainicjowaniu CU- 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
6 DA. Następnie opróżnimy wszystkie bufory. 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3
7 d_data[ tid ] = 17; Możemy teraz spróbować uruchomić pro-
gram. Jeśli nie popełniliśmy żadnego błędu, Bardziej praktyczny przykład funkcji jądra
przedstawimy w artykule, który ukaże się w
następnym numerze SDJ. Omówimy w nim
Więcej w Sieci również sposoby optymalizacji kodu CUDA.
• http://www.nvidia.com/object/cuda_home.html – Oficjalna strona NVIDIA CUDA;
• http://developer.nvidia.com/object/gpu_computing_online.html – materiały edukacyjne
dotyczące CUDA; MICHAŁ MATUSZAK
• http://developer.download.nvidia.com/compute/cuda/2_3/toolkit/docs/NVIDIA_CUDA_ Doktorant na Wydziale Matematyki i Informatyki
Programming_Guide_2.3.pdf – duży (145 stronicowy) przewodnik dla programistów ko-
Uniwersytetu Mikołaja Kopernika w Toruniu. Je-
rzystających z CUDA;
• http://developer.download.nvidia.com/compute/cuda/2_3/toolkit/docs/CUDA_Reference_ go główne zainteresowania to dynamiczne sie-
Manual_2.3.pdf – dokumentacja wszystkich funkcji CUDA i towarzyszącego mu pakietu ci gaussowskie i sieci przekonań oraz programo-
pomocniczego; wanie równoległe.
• http://developer.download.nvidia.com/compute/cuda/2_3/toolkit/docs/NVIDIA_CUDA_Be-
stPracticesGuide_2.3.pdf – zbiór tzw. najlepszych rozwiązań (ang. best practices) w pro- JACEK MATULEWSKI
gramowaniu z użyciem CUDA;
• http://www.nvidia.com/object/cuda_get_samples.html – dydaktyczne przykłady użycia Fizyk zajmujący się na co dzień optyką kwantową
CUDA do przyspieszenia różnego typu obliczeń; i układami nieuporządkowanymi na Wydziale Fizy-
• http://www.nvidia.com/object/cuda_home.html – praktyczne przykłady użycia CUDA, w tym ki, Astronomii i Informatyki Stosowanej Uniwersyte-
również nadsyłane przez użytkowników. Można tam zgłaszać także własne programy bądź tu Mikołaja Kopernika w Toruniu. Jego specjalnością
publikacje. Większość prac zawiera współczynnik, o jaki został przyspieszony ich program. są symulacje ewolucji układów kwantowych od-
działywujących z silnym światłem lasera.
Od 1998 interesuje się programowaniem dla sys-
temu Windows. Ostatnio zainteresowany platfor-
Więcej w książce mą .NET i językiem C#, a także XNA. Wierny użyt-
Artykuł jest fragmentem dodatku dołączonego do książki Visual C++. Gotowe rozwiązania dla
programistów Windows, która wkrótce ukaże się nakładem Wydawnictwa Helion. kownik kupionego w połowie lat osiemdziesią-
tych "komputera osobistego" ZX Spectrum 48k.

66 12/2009
Warsztaty

AJAX w jQuery
Jak stworzyć efektowną galerię w AJAX-ie z wykorzystaniem
frameworka jQuery cz. 1.
Coraz więcej stron w Internecie wykorzystuje technologię AJAX. Jej
znajomość nie jest już wyjątkowym atutem programisty, a coraz częściej
jedną z podstawowych umiejętności wymaganych praco- i zleceniodawców.
Można też znaleźć wiele rozwiązań ułatwiających pisanie programów
wykorzystujących AJAX-a. Jednym z nich jest framework jQuery.
funkcji.. W przykładzie powyżej ciało funkcji
Dowiesz się: Powinieneś wiedzieć: mamy dopiero wpisać. W obecnej postaci ten
• Jak dołączyć skrypt napisany w jQuery do • Jak tworzyć strony w języku (X)HTML; kod nie robi jeszcze nic.
strony WWW; • Jak formatować stronę WWW wykorzystując Teraz umieścimy tam kod odpowiedzialny
• Co to są funkcje anonimowe; CSS; za reakcję na najechanie kursorem myszy na
• Jak wyświetlić grafikę w oryginalnym roz- • Znać podstawy PHP i MySQL. miniaturę obrazka:
miarze bez przeładowania strony;
• Jak stworzyć system oceniania zdjęć wyko- $('#index img').mouseenter(function()
rzystujący efekty jQuery; {
• Jak dynamicznie modyfikować wygląd strony. });

Tym razem jako argument funkcji $() używa-


wrotna, funkcje te nie będą widoczne w my selektora elementu, którego dana akcja ma
miejscu ich wywołania. Oba pliki w tym dotyczyć. W naszym przypadku jest to dowol-
Poziom przykładzie zostały umieszczone w kata- ny znacznik <img /> znajdujący się wewnątrz
trudności logu js. elementu o identyfikatorze index (id=”in-
Tworzymy teraz plik galeria.js, a w pliku dex” w znaczniku w kodzie strony). Selekto-
umieszczamy następujący kod: ry w jQuery są niemal identyczne jak w css, do
funkcji przekazujemy je w apostrofach lub cu-

N
a początku przygotujmy standardo- $(document).ready(function() dzysłowiu. Na zwróconym obiekcie wywoły-
wą stronę z prostą galerią działającą { wane jest metoda mouseenter(), która w reak-
w tradycyjny sposób, czyli z przeła- }); cji na najechanie kursorem myszy na umiesz-
dowaniem całej strony w momencie kliknię- czony w obiekcie element wykonuje kolej-
cia kolejnej miniaturki. Przykładowa galeria, Jest to podstawowy szkielet skryptu wyko- ną funkcję anonimową. Wewnątrz tej funkcji
którą będziemy modyfikować w tym artyku- rzystującego framework jQuery. Symbol $ jest umieścimy kod odpowiedzialny za pobranie
le, może wyglądać tak jak na Listingu 1. aliasem funkcji jQuery() (wszystkie opera- dużego pliku obrazka z serwera i załadowanie
Aby móc wczytywać obrazki w galerii bez cje frameworka opierają się na wywołaniu tej go do odpowiedniego miejsca na stronie.
przeładowania strony, a z użyciem AJAX-a, mu- funkcji, tak więc używanie aliasu $ daje wie-
simy dołączyć odpowiednie pliki do sekcji head le korzyści – przyspiesza pisanie kodu i czy- var plik=$(this).attr('id');
naszej strony. Może to wyglądać w ten sposób: ni go bardziej przejrzystym). Argumentem tej $('#image').load('obraz.php?id='+plik);
funkcji jest obiekt dokument. Funkcja zwra-
<script type ="text/javascript" src="js/ ca własny obiekt, na którym z kolei wywoła- Na początku tworzymy zmienną plik i wczy-
jquery-1.3.2.js"></script> na zostaje metoda ready(). Funkcja będąca ar- tujemy do niej poprzez metodę attr wartość
<script type="text/javascript" src="js/ gumentem metody ready() zostanie urucho- parametru id elementu, na który najechano
galeria.js"></script> miona od razu po wczytaniu przez przeglądar- kursorem myszy. Wartość ta jest jednocze-
kę drzewa dokumentu (DOM), czyli całego ko- śnie nazwą pliku z dużą grafiką bez rozszerze-
W pierwszej linijce dołączamy plik z ak- du html/xhtml. nia. Ponieważ elementów odpowiadających se-
tualną wersją jQuery. W linijce poniżej Używając jQuery, najczęściej będziemy two- lektorowi #index img jest kilka, posłużymy
włączamy plik, który za chwilę utworzy- rzyć funkcje anonimowe. Są to funkcje, które się referencją do obiektu this, która wskazu-
my. Bardzo ważna jest kolejność włącze- nie posiadają swojej nazwy, a ich definicja je na ten konkretny obiekt, dla którego wy-
nia plików, gdyż w pliku galeria.js będzie- umieszczana jest w miejscu wywołania. Funk- wołano metodę mouseenter (odpowiadają-
my korzystać z funkcji umieszczonych w cję taką zapisujemy wyrażeniem function(), cy miniaturce najechanej kursorem myszy).
jquery-1.3.2.js i jeżeli kolejność byłaby od- a po nim w nawiasach {} umieszczamy ciało W kolejnej lini posłużymy się metodą wyko-

68 12/2009
AJAX w jQuery

rzystującą technologię AJAX. Jest to metoda Teraz obrazek wprawdzie pokazuje się w efek- fadeIn(600). Rola argumentu tych metod
load , która do elementu wskazanego selekto- towny sposób, ale przestrzeń przeznaczona na jest w każdym przypadku taka sama.
rem #image wczyta element zwrócony przez stronie na ten obrazek znika i pojawia się wraz Pozostało nam jeszcze wyłączenie prze-
skrypt php wskazany w argumencie meto- z obrazkiem, co daje niezbyt atrakcyjny rezul- ładowania strony po przypadkowym klik-
dy, czyli ‘obraz.php?id=’+plik (do skryptu tat. Aby zapobiec temu efektowi, umieśćmy nięciu w miniaturkę przy działającym Java-
obraz.php przekazujemy metodą GET zmien- wewnątrz bloku div o identyfikatorze image Script. Wewnątrz głównej funkcji naszego
ną id o wartości przekazanej przez zmienną dodatkowy blok div, a kod jQuery zmodyfikuj- kodu jQuery umieszczamy wywołanie me-
plik). Aby operacja się powiodła, musimy na- my tak, by przyjął następującą postać: tody click() dla obiektu zwróconego przez
pisać odpowiedni skrypt php. W katalogu, w $('#index img')
którym znajduje się plik z kodem html/xhtml, $('#image div').hide();
tworzymy plik obraz.php o zawartości pokaza- $('#image div').load('obraz.php',{'id': $('#index img').click(function()
nej poniżej. plik}); {
$('#image div').show(500); return false;
<?php });
echo '<img src="galeria/'.$_ Sam efekt pokazywania się grafiki możemy
GET['id'].'.jpg" zmienić na inny, używając zamiast meto- Instrukcja return false ; powoduje, że akcja
alt="'.$_ dy show(500), metody slideDown(700) lub przeglądarki związana z kliknięciem elemen-
GET['id'].'" />';
?>
Listing 1. Przykładowa galeria. Aby wyświetlić klikniętą grafikę w oryginalnym rozmiarze, strona
musi zostać przeładowana
Skrypt wyświetla poprzez instrukcję echo
znacznik img wskazujący na plik obrazka w <?php
katalogu galeria. Nazwy pliku używamy też echo '<?xml version="1.0" encoding="utf-8"?>';
jako tekstu alternatywnego obrazka – atry- ?>
but alt . Zmienną możemy też przesłać me- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/
todą POST. Skrypt php będzie wówczas wy- DTD/xhtml11.dtd">
glądał tek: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl">
<head>
<?php <meta http-equiv="content-type" content="text/html; charset=utf-8" />
echo '<img src="galeria/'.$_ <title>Galeria</title>
POST['id'].'.jpg" <link href="style.css" rel="stylesheet" type="text/css" />
alt="'.$_ <script type ="text/javascript" src="js/jquery-1.3.2.js"></script>
POST['id'].'" />'; <script type="text/javascript" src="js/galeria.js"></script>
?> </head>
<body>
Natomiast wywołanie metody load w skryp- <h1>Galeria</h1>
cie jQuery modyfikujemy w ten sposób: <div id="content">
<div id="index">
$('#image').load('obraz.php',{'id':plik}); <div><a href="galeria.php?img=p1"><img id="p1" src="galeria/min/p1.jpg"
alt="niewiadomoco" /></a></div>
Zmienna id jest przekazywana w meto- <div><a href="galeria.php?img=p2"><img id="p2" src="galeria/min/p2.jpg"
dzie jako dodatkowy argument w postaci alt="niewiadomoco" /></a></div>
{‘klucz’: ‘wartość’} <div><a href="galeria.php?img=p3"><img id="p3" src="galeria/min/p3.jpg"
Projekt jest w zasadzie gotowy, ale moż- alt="wkręt" /></a></div>
na go jeszcze poprawić i uatrakcyjnić. Przede <div><a href="galeria.php?img=p4"><img id="p4" src="galeria/min/p4.jpg"
wszystkim jeżeli korzystamy już z framewor- alt="schody" /></a></div>
ka jQuery, wypadałoby skorzystać z efektów, <div><a href="galeria.php?img=p5"><img id="p5" src="galeria/min/p5.jpg"
które oferuje przy ładowaniu nowej zawar- alt="sprężyna" /></a></div>
tości. W tym celu przed wywołaniem meto- </div>
dy load ukryjemy metodą hide() element o <div class="clear"></div>
identyfikatorze #image tak, by po wczytaniu <div id="image">
do niego obrazka nie był on od razu widocz- <?php
ny na stronie. if(isset($_GET['img']))
{
$('#image').hide(); echo'<img src="galeria/'.$_GET['img'].'.jpg" alt="'.$_GET['img'].'"
/>';
Natomiast po wczytaniu obrazka wywoła- }
my metodę show(500), by ten obrazek efek- ?>
townie pokazać. Argument w nawiasie jest </div>
czasem trwania efektu w milisekundach i </div>
może być modyfikowany. </body>
</html>
$('#image').show(500);

www.sdjournal.org 69
Warsztaty

tu zostanie zatrzymana, czyli w tym przypad- sób tradycyjny. W ten oto sposób zakończyli- będą zachowywane w bazie danych. Kod two-
ku przeglądarka nie wczyta strony spod wska- śmy etap pierwszy. Kompletny kod przykładu rzący odpowiednią tabelę w bazie MySql moż-
zanego adresu. Natomiast jeżeli przeglądarka został umieszczony na płycie. na znaleźć w pliku oceny.sql. W kodzie strony,
nie będzie z jakiś przyczyn obsługiwała języka Kolejnym etapem tworzenia galerii będzie tuż przed ostatnim zamykającym znacznikiem
JavaScript, galeria będzie nadal działała w spo- dodanie systemu ocen każdego zdjęcia. Oceny </div>, dopisujemy kod z Listingu 2. Dodamy
też kilka definicji styli (Listing 3).
Listing 2. Wczytanie średniej oceny z bazy danych dla wyświetlonego zdjęcia, oraz kod formularza Oprócz tego tworzymy w pliku rate.php
oceny skrypt zapisujący oceny z formularza (Listing
4). Skrypt działa poprawnie. Teraz zróbmy
<div id="rate"> to samo w AJAXie. Zaczniemy od wczyta-
<?php nia oceny równocześnie z obrazkiem po naje-
if(isset($_GET['img'])) chaniu kursorem myszy na miniaturkę. Usu-
{ wamy linię:
$connect=mysql_connect('127.0.0.1', 'root', '');
mysql_select_db('test', $connect); $('#image div').load('obraz.php',{'id':
$result=mysql_query("SELECT AVG(ocena) FROM oceny WHERE grafika='".$_ plik});
GET['img']."'", $connect);
$rate=mysql_fetch_row($result); a w jej miejsce wstawiamy kod:
mysql_close($connect);
for($i=1; $i<=round($rate[0]); $i++) $.get('obraz.php', {id:plik},
{ function(data)
echo '<img src="img/punkt.gif">'; {
} }, 'xml');
}
?> Jest to wywołanie kolejnej metody AJAXowej
</div> – get() na obiekcie jQuery. Metoda przyjmu-
<form class="clear" action="rate.php" method="post"> je cztery argumenty: adres skryptu, zmienne
<div> przekazywane do skryptu metodą get, funk-
<strong>Twoja ocena:</strong> cję przetwarzającą zwracane przez metodę da-
<select name="rate"> ne i typ danych zwracanych przez metodę.
<option value="1">1</option> W tym ostatnim parametrze możemy wskazać
<option value="2">2</option> jeden z typów: xml, html, script, json, jsonp, lub
<option value="3">3</option> text. Wszystkie argumenty poza pierwszym są
<option value="4">4</option> opcjonalne. W naszym przykładzie posłużymy
<option value="5">5</option> się formatem xml. Musimy w tym celu od no-
</select> wa napisać skrypt obraz.php. Usuwamy starą za-
<input type="hidden" name="img" value="<?php echo $_GET['img']?>"> wartość, a na początku skryptu wysyłamy od-
<input type="submit" value=">>" /> powiedni nagłówek i wypisujemy definicję do-
</div> kumentu xml
</form>
header("Content-type: text/xml");
Listing 3. Definicje stylów galerii echo '<?xml version="1.0" encoding="UTF-
form 8"?>';
{
margin: 10px 0; Następnie łączymy się z bazą danych i pobie-
} ramy ocenę wybranej grafiki

select $result=mysql_query("SELECT AVG(ocena)


{ FROM oceny WHERE
width: 40px; grafika='".$_
margin: 10px 4px; GET['id']."'",
} $connect);
#rate $rate=mysql_fetch_row($result);
{
width: 240px; Na końcu wypisujemy dane w formacie xml
margin: 0 auto;
} echo '<galeria><src>galeria/'.$_GET
['id'].'.jpg</src><alt>'.$_GET
#rate > img ['id'].'</alt> <rate>'.$rate[0].'
{ </rate></galeria>';
float: left;
} Wracamy do pliku galeria.js i uzupełnia-
my funkcję zwrotną w wywołaniu metody

70 12/2009
AJAX w jQuery

get (data). Argument data jest nazwą zmien- $('#image div').empty(); Jeżeli ocena zawiera część ułamkową,
nej, pod którą dostępne będą dane zwrócone $('#rate').empty(); to wstawiamy dodatkowy punkt, nada-
przez tę metodę. jąc mu identyfikator id=”fade”, a metodą
Usuwamy starą zawartość elementów „<div fadeTo(1,fade) nadajemy mu stopień prze-
var src=$(data).find('src').text(); id=”image”><div></div></div>” i „<div zroczystości równy części ułamkowej oce-
var alt=$(data).find('alt').text(); id=”rate”></div>”. ny. Czas trwania efektu ustawiamy na 1 mi-
var rate=$(data).find('rate').text(); lisekundę.
$('#image div').append('<img src="'+src+'" Teraz przejdziemy do zapisywania ocen rów-
Poprzez metodę find() odnajdujemy odpo- alt="'+alt+'" />'); nież z wykorzystaniem AJAX-a. Na początku
wiednie elementy i wstawiamy ich zawar- for(var i=0; i<count; i++) ukryjemy przycisk submit formularza. Zapisa-
tość do utworzonych zmiennych. { nie oceny odbędzie się bezpośrednio po wybra-
$('#rate').append('<img src= niu wartości z listy. Przycisk ukryjemy od razu
var count=parseInt(rate); "img/punkt.gif">'); po załadowaniu strony; z tego powodu poniż-
var fade=rate-count; } szą linijkę wstawimy bezpośrednio wewnątrz
metody ready().
Pobierana ocena (zmienna rate) jest średnią Metodą append() wstawiamy nowy element
z ocen cząstkowych i najczęściej nie będzie img z dużym obrazkiem oraz elementy img z $("input[type='submit']").hide();
liczbą całkowitą. W wersji bez jQuery ocena punktami w ilości odpowiadającej wartości
była po prostu zaokrąglana według reguł ma- zmiennej count . Po wybraniu oceny z listy musimy wykonać
tematycznych. dwie czynności.
Tutaj możemy część ułamkową pokazać if(fade)
poprzez nadanie odpowiedniego stopnia { • odpowiednia wartość ma zostać zapisa-
przeźroczystości ostatniego punktu. W tym $('#rate').append('<img id= na do bazy;
celu tworzymy zmienną count z częścią cał- "fade" src="img/punkt.gif">'); • ocena pod obrazkiem powinna zostać
kowitą oceny i zmienną fade z częścią ułam- $('#fade').fadeTo(1,fade); zaktualizowana.
kową. }
Obydwie czynności będą realizowane w wy-
Listing 4. Zapisanie oceny cząstkowej zdjęcia w bazie danych wołaniu metody change() obiektu utworzo-
<?php nego z elementu <select>. Na początku two-
$connect=mysql_connect('127.0.0.1', 'root', ''); rzymy dwie zmienne: plik i selectrate.
mysql_select_db('test', $connect); Zmienna plik zawiera nazwę ocenianego pli-
$result=mysql_query("INSERT INTO oceny ('grafika','ocena') VALUES('".$_ ku bez rozszerzenia pobraną z atrybutu alt
POST['img']."','".$_POST['rate']."')", $connect); znacznika <img>, a do zmiennej selectrate
mysql_close($connect); podstawiamy ocenę wybraną z listy rozwija-
header('Location: galeria.php?img='.$_POST['img']); nej. Z funkcjonalności AJAX-a skorzystamy
?> tym razem za pośrednictwem metody post().
Jest ona niemal identyczna z metodą get().
Listing 5. Zmiana oceny obrazka po dodaniu kolejnej oceny cząstkowej. Metoda post() przyjmuje Różni ją jedynie metoda wysyłania zmiennych
nazwę pliku ze skryptem php do wywołania, komplet zmiennych wysyłanych metodą post i definicję do skryptu na serwerze. Tym razem do skryp-
funkcji zwrotnej, w której dane zwrócone przez metodę post() zostaną przetworzone i umieszczone w
odpowiednim miejscu na stronie
tu wysyłamy trzy zmienne: img, rate i ajax .
Zawartość funkcji zwrotnej nie prezentuje ni-
$('select').change(function() czego nowego (Listing 5).
{ Modyfikacja skryptu rate.php polega głów-
var plik=$('#image img').attr('alt'); nie na sprawdzeniu, czy został on wywołamy
var selectrate=$('select option:selected').text() poprzez AJAX-a, i w takim wypadku wypisa-
$.post('rate.php', {img: plik, rate: selectrate, ajax: 1}, function(data) niu danych w formacie xml. Kompletny kod
{ przykładu został umieszczony na płycie.
var rate=$(data).find('rate').text(); Nie są to oczywiście wszystkie możliwo-
var count=parseInt(rate); ści jQuery związane z AJAX-em. Ale nawet
var fade=rate-count; to, co przedstawiłem w tym artykule, pozwo-
$('#rate').empty(); li na wykonanie ciekawych i dobrze działają-
for(var i=0; i<count; i++) cych efektów.
{
$('#rate').append('<img src="img/punkt.gif">');
}
if(fade) LESZEK SEWASTIANOWICZ
{ Doświadczony trener IT,
$('#rate').append('<img id="fade" src="img/punkt.gif">'); programista, redaktor ebiu-
$('#fade').fadeTo(1,fade); letynu „Sekrety programi-
} stów”, profesjonalista skon-
}); centrowany na rozwiąza-
}); niach. Więcej znajdziesz na
www.serwan.pl.

www.sdjournal.org 71
Efektywność programowania

Skromny programista
O nieprzecenianiu własnych możliwości
Ponad trzydzieści lat temu Edgar Dijkstra w swoim przemówieniu „The
humble programmer”, stwierdził, że ludzkie czaszki są zbyt małe, by
poradzić sobie z problemami programistycznymi. Zadziwiające jest to,
jak praktyczny wydźwięk ma to stwierdzenie.

W
ielu z nas wydaje się działać tak, czasu, tylko dlatego, że nie potrafią napisać go Cały czas CZYTAMY kod! To, jak on wyglą-
jak by możliwości mózgu by- lepiej. Mają zbyt duży bałagan w głowach. da, ma ogromny wpływ na to, jak efektywnie
ły nieograniczone. Tworzymy Na przestrzeni lat inżynieria oprogramowania pracujemy. Spójrzmy na poniższy przykład:
ogromne klasy, metody, skomplikowane algo- stworzyła wiele narzędzi, które mogą pomóc w w tym arty przed. kilkabardzoprostych Technik,
rytmy, wierząc, że to właśnie jest esencja pro- opanowaniu złożoności kodu. Oto kilka z nich: ktore mogą się przycz. znacząco do zw. pr.iczytelno-
gramowania. Im bardziej złożone rozwiąza- ści tw. kodu satakprosteze możecisię Wydawać że Ich
nie stworzymy, tym większe mamy zadowo- • dekompozycja – jedna z podstawowych st.wpraktyce NiewieElezmieninic bardziejmylnego-
lenie z tego, co zrobiliśmy. Nie ważne, że im zasad – dziel i rządź, większe problemy towłaśnieprostotajestichsiłą akons.uż.pow.ogromne
dłużej pracujemy z takim kodem, tym trud- dziel na mniejsze; zmianywTworzonymkodzie.
niej się po nim poruszać, trudniej dokony- • testy – posiadanie testów jednostko- O co chodzi? Jest to dokładnie tekst jedne-
wać zmian i najchętniej byśmy do niego nie wych zwalnia Cię od analizowania i śle- go z wcześniejszych akapitów. Jak Ci się go
zaglądali. Tworzymy nieczytelne rozwiąza- dzenia konsekwencji wprowadzanych czyta? Ile czasu zajęło Ci zrozumienie tego
nia! I nie jest to kwestia teorii, która ma nie- zmian na istniejący już kod; tekstu? Czy w ogóle udałoby Ci się go prze-
wiele wspólnego z rzeczywistością. Jest to jak • konwencje nazewnicze – raz ustanowio- czytać, gdybyś nie znał oryginału? Ile czasu
najbardziej praktyczna praktyka. ne zasady nazywania elementów w kodzie potrzebowałbyś na wprowadzenie zmiany?
Przyznajmy się do tego, że nasz mózg jest pozwalają Ci nie zastanawiać się nad nimi Powyższy przykład może wydawać się ab-
zbyt ograniczonym narzędziem, aby pora- za każdym razem, kiedy tworzysz nową surdalny! Bo jest! Tak samo jak kod tworzony
dzić sobie z całą złożonością, która pojawia zmienną, klasę, metodę czy pakiet; przez wielu z nas. Kiedy następnym razem bę-
się podczas tworzenia systemów informatycz- • proste i nieduże metody, klasy – im mniej- dziesz miał okazję stworzyć nieczytelne roz-
nych. Kiedy uświadomimy sobie, że nie jeste- sze, tym prostsze, tym łatwiejsze w two- wiązanie – przypomnij sobie ten antyprzykład,
śmy mistrzami programowania, posiadający- rzeniu, utrzymaniu i zrozumieniu; szybko nabierzesz ochoty, aby dokonać zmian.
mi supermózgi, życie programistyczne stanie • posługiwanie się słownictwem z dziedzi- A przecież programiści tworzą swoje dzieła w
się prostsze. Mówił o tym już Dijkstra w 1972 ny problemu – jeśli głównymi nazwami w języku programowania. Chcemy podkreślić tu-
roku, zaś ostatnio prawda ta kryje się m. in. w Twojej aplikacji będą słowa domenowe, ła- taj słowo „język”. Napisany kod źródłowy jest ni-
zasadzie KISS (ang. keep it simple stupid) upo- two będzie skojarzyć ich sens i znaczenie z czym innym jak wypowiedzią w języku formal-
wszechnianej wraz z metodykami Agile oraz wymaganiami użytkownika i ostatecznym nym, jest formą komunikacji. Jak zatem warto
związanej z ruchem Software Craftsmanship. zastosowaniem tworzonego systemu; się komunikować?
Prostota przede wszystkim. Im prostsze bę- • refaktoryzacja – wiele pomysłów na to,
dzie rozwiązanie, które tworzymy, tym mniej- jak można uprościć już napisany kod. Przykład
sze będzie obciążenie mózgu, pamięci pod- Przykład pozwoli wychwycić kilka mechani-
ręcznej, tym łatwiejsze do opanowania, tym Ręce na klawiaturę zmów przydatnych w pracy. Napiszemy kla-
lepsze programy będziemy pisać. Jak stwier- W tym artykule przedstawimy kilka wybra- sę, której zadaniem jest dokonywać transfor-
dza Steve McConnell w książce Programista nych i bardzo prostych technik, które mogą macji danych dotyczących osoby zapisanych
doskonały (ang. Code complete), programiści się przyczynić znacząco do zwiększenia pro- w płaskim pliku tekstowym do danych zapi-
piszą nieczytelny kod nie dlatego, że nie mają stoty i czytelności Twojego kodu. Są tak pro- sanych w formie XML. Przykładowe dane za-
ste, że może Ci się wydawać, iż ich stosowa- warte są na Listingach 1 i 2.
nie w praktyce niewiele zmieni. Nic bardziej Listing 3 zawiera przykładową implementa-
Listing 1. Dane dotyczące osoby zapisane
mylnego! To właśnie prostota jest ich siłą, a cję takiej transformacji w języku Java. Chcieli-
w płaskim pliku
konsekwentne używanie powoduje ogromne byśmy podkreślić, iż sam język programowa-
Jan Kowalski 67020101234 "Piłsudskiego zmiany w tworzonym kodzie.
30/22" Łódź
Piotr Nowak 82110110123 "Piotrkowska Przede wszystkim czytelność Skromny programista
Przyznajmy się do tego, że nasz mózg jest
60/12" Poznań Nieprawdą jest, że pisanie kodu zajmuje pro- zbyt ograniczonym narzędziem, aby po-
Krzysztof Kwiatkowski 78030204321 gramistom najwięcej czasu! Większość ener- radzić sobie z całą złożonością, która po-
"Mickiewicza 3/ gii poświęcamy na czytanie kodu i to najczę- jawia się podczas tworzenia systemów in-
14" Warszawa ściej swojego, wtedy kiedy go poprawiamy, formatycznych.
szukając błędu czy dodając kolejną metodę.

72 12/2009
Skromny programista

nia nie odgrywa tutaj tak istotnego znaczenia, nia. Chcemy dążyć do tego, żeby kod można by- kie zamknięte zadanie i wyodrębniamy z nie-
a raczej pełni rolę drugoplanową. Zdecydowa- ło czytać niczym książkę. Pierwszym krokiem, go osobą, zazwyczaj prywatną metodę.
nie ważniejsze są uniwersalne reguły opisane który wykonamy będzie dekompozycja algoryt- Jedna w wyodrębnionych metod writeToXml
poniżej, które z powodzeniem można stosować mu. Być może pamiętasz ze studiów lub z ksią- mogłaby wyglądać jak na Listingu 5.
w każdym języku i w każdej technologii. żek dotyczących algorytmiki, algorytmy zapisy-
W powyższym kodzie użyto obiektu klasy wane w formie pseudokodu lub języka natural- DRY czyli Don't Repeat
Scanner do odczytu danych z płaskiego pliku nego. Wydawać by się mogło, że poza teoretycz- Yourself czyli unikaj powtórzeń
tekstowego i jego metod, które potrafią parso- nym kontekstem, metoda ta nie ma zastosowa- To co wyraźnie rzuca się w oczy na Listingu
wać tekst i dokonywać konwersji do konkret- nia. Wręcz przeciwnie, jest to najbardziej prak- 5. to kilkanaście instrukcji o bardzo podob-
nego typu prostego. Jednocześnie za pomocą tyczna z praktycznych zasad programowania nej budowie np.
klasy BufferedWriter następuje zapisywa- – każdy z algorytmów, zapisz w formie kroków,
nie danych w formacie XML. Ponadto meto- które się na niego składają. W naszym prostym bufferedWriter.write(
da zgłasza wyjątek ApplicationException w przykładzie kroki będą następujące: " <imie>" + person.getName() +
przypadku niepowodzenia metody. "</imie>");
Przedstawiony kod realizuje założony cel • przygotuj strumień do czytania; bufferedWriter.newLine();
– dokonuje transformacji z jednego formatu • wczytaj dane o osobach;
do drugiego. Całe rozwiązanie zostało zawarte • zamknij strumień odczytu; Powtórzenia tego typu niosą ze sobą zagro-
w metodzie o długości około 80 wierszy. Wystę- • przygotuj strumień do zapisu; żenia – jeśli będziemy chcieli zmienić spo-
puje w niej 12 zmiennych lokalnych i 2 parame- • zapisz dane w formacie XML; sób zapisu, będziemy musieli zmienić kil-
try, ponadto jest kilka instrukcji warunkowych, • zamknij strumień zapisu. kanaście wierszy. Poza tym łatwo zapo-
złożona pętla i kilkanaście wierszy związanych mnieć o takich zmianach. Zamiast wielo-
z obsługą wyjątków. Chleb powszedni każdego Proste, prawda? A co gdyby nasz kod wyglą- krotnego używania podobnego zestawu
programisty. Zakłada się, że mózg jest w stanie dał podobnie, tylko zapisany w języku pro- konstrukcji, wprowadźmy pomocniczą me-
zapamiętać w danym momencie od 5 do 9 in- gramowania, którego używasz. Implementa- todę, która będzie zapisywać jeden wiersz z
formacji. Nawet w pogłębionym transie progra- cja w języku Java mogłaby wyglądać tak jak użyciem obiektu typu BufferedWriter, za-
mistycznym (patrz poprzedni artykuł o Strefie na Listingu 4.
zero), ilość informacji, którą jesteśmy bezproble- W celu uwypuklenia analogii między al- Listing 2. Dane dotyczące osoby zapisane w
mowo przetwarzać jest tylko nieco większa. Tak gorytmem zapisanym w formie pseudoko- formacie XML
długa metoda ogromnie pamięć programisty w du (lub języka naturalnego) a kodem źródło-
momencie czytania i wprowadzania zmian w wym, dodaliśmy komentarze. W kodzie pro- <?xml version="1.0" encoding="UTF-8"?>
kodzie. Choć możliwe jest pracowanie z takim dukcyjnym nie są one potrzebne, gdyż kod za- <osoby>
kodem, jest to duże obciążenie dla naszego mó- pisany w ten sposób, jest w większości przy- <osoba>
zgu, które z czasem zmniejsza wydajność naszej padków samodokumentujący. Jest do dodat- <imie>Jan</imie>
pracy z kodem. Prawdę mówiąc, nie warto za- kowa zaleta tej techniki. Odpowiednio na- <nazwisko>Kowalski</nazwisko>
męczać naszej pamięci podręcznej tak złożony- zwane metody, są jednocześnie dokumenta- <pesel>67020101234</pesel>
mi konstrukcjami. cją stworzonego rozwiązania. <ulica>Piłsudskiego 30/22
Głównym problemem w powyższym przy- </ulica>
kładzie jest to, iż instrukcje związane z zapi- Dwie strategie <miasto>Łódź</miasto>
sem i odczytem przeplatają się, co czyni kod Metodę dekompozycji algorytmu możemy <dataurodzenia>środa, 1 luty
bardzo trudnym w czytaniu. Cały algorytm zastosować na dwa sposoby: 1967</dataurodzenia>
został sprowadzony do jednego szeregu in- </osoba>
strukcji. • na początku – przystępując do tworzenia <osoba>
metody, najpierw zapisujemy ją w postaci <imie>Piotr</imie>
Dekompozycja algorytmu kroków, a następnie uszczegóławiamy; <nazwisko>Nowak</nazwisko>
Co zatem możemy zrobić? Dzielić na mniejsze • poprzez refaktoryzację – nie zawsze na <pesel>82110110123</pesel>
części, tak aby zmniejszyć złożoność rozwiąza- początku algorytm jest oczywisty, czasa- <ulica>Piotrkowska 60/12</ulica>
mi podczas jego implementacji pojawia- <miasto>Poznań</miasto>
ją się sytuacje, które nie do końca przewi-
Trzy techniki upraszczające kod <dataurodzenia>poniedziałek, 1
dzieliśmy, a czasami mamy do czynienia listopad 1982</
• Dekompozycja algorytmu – zapisz me- z odziedziczonym kodem, który jest nie- dataurodzenia>
tody w postaci kroków algorytmu, najlepiej napisany, wtedy możemy użyć </osoba>
a szczegóły umieść w metodach po- refaktoryzacji. <osoba>
mocniczych; stosuj tę zasadę tak długo,
<imie>Krzysztof</imie>
aż metody pomocnicze staną się proste
i łatwe w zrozumieniu. W powyższym przykładzie zastosowaliśmy <nazwisko>Kwiatkowski</nazwisko>
• Wyodrębnianie metody – metody po- właśnie tę drugą strategię. Mieliśmy gotowe, <pesel>78030204321</pesel>
mocnicze twórz z użyciem refakto- monolityczne rozwiązanie i zdekomponowa- <ulica>Mickiewicza 3/14</ulica>
ryzacji wyodrębnianie metody - wiele liśmy je na mniejsze, łatwiejsze w zarządzaniu <miasto>Warszawa</miasto>
środowisk programistycznych wspie- części. Użyliśmy do tego celu jedną z podstawo- <dataurodzenia>czwartek,
ra tę operację.
• Usuwanie powtórzeń – unikaj wszel-
wych refaktoryzacji – wyodrębnienie metody. 2 marzec 1978</dataurodzenia>
kich powtórzeń, gdyż z czasem stają </osoba>
się one przyczyną wielu błędów, któ- Wyodrębnienie metody </osoby>
re trudno wychwycić. Refaktoryzacja ta jest prosta – odnajdujemy
spójny fragment metody realizujący niewiel-

www.sdjournal.org 73
Efektywność programowania

pewniając odpowiednią ilość wcięć, któ- private void writeLine gdzie line oznacza pojedynczy wiersz za-
re sprawiają, że wynikowy plik XML jest (BufferedWriter bufferedWriter, pisywany w pliku, zaś indentLevel to
czytelny.: String line, Indent indentLevel) { głębokość wcięć w pliku XML, które są

Listing 3. Pierwsza wersja klasy PersonDataTransformer

public class PersonDataTransformer { bufferedWriter.write(" <ulica>" + street


public void transformFromPlainToXmlFile(String + "</ulica>");
plainFileName, bufferedWriter.newLine();
String xmlFileName) { scanner.useDelimiter("\\s");
File file = new File(plainFileName); scanner.next();
Scanner scanner = null; String city = scanner.next();
BufferedWriter bufferedWriter = null; bufferedWriter.write(" <miasto>" + city +
try { "</miasto>");
scanner = new Scanner(new FileInputStream(file), bufferedWriter.newLine();
"UTF-8"); String stringDate = pesel.substring(0, 6);
File outputXml = new File(xmlFileName); SimpleDateFormat simpleDateFormat = new SimpleDa
bufferedWriter = new BufferedWriter(new teFormat("yyMMdd");
OutputStreamWriter( Date date = simpleDateFormat.parse(stringDate);
new FileOutputStream(outputXml), "UTF8")); bufferedWriter.write(" <dataurodzenia>"
bufferedWriter.write("<?xml version=\"1.0\" + DateFormat.getDateInstance(DateFormat.F
encoding=\"UTF-8\"?>"); ULL).format(date)
bufferedWriter.newLine(); + "</dataurodzenia>");
bufferedWriter.write("<osoby>"); bufferedWriter.newLine();
bufferedWriter.newLine(); bufferedWriter.write(" </osoba>");
while (scanner.hasNext()) { bufferedWriter.newLine();
bufferedWriter.write(" <osoba>"); if (scanner.hasNextLine()) {
bufferedWriter.newLine(); scanner.nextLine();
String name = scanner.next(); }
bufferedWriter.write(" <imie>" + name + }
"</imie>"); bufferedWriter.write("</osoby>");
bufferedWriter.newLine(); } catch (IOException ioe) {
String secondName = scanner.next(); throw new ApplicationException(ioe);
bufferedWriter.write(" <nazwisko>" + } catch (ParseException pe) {
secondName throw new ApplicationException(pe);
+ "</nazwisko>"); } finally {
bufferedWriter.newLine(); if (bufferedWriter != null) {
String pesel = scanner.next(); try {
bufferedWriter.write(" <pesel>" + pesel + bufferedWriter.close();
"</pesel>"); } catch (IOException e) {
bufferedWriter.newLine(); throw new ApplicationException(e);
if (pesel.length() != 11) { }
System.out.println("Pesel musi mieć 11 }
znaków"); if (scanner != null) {
break; scanner.close();
} }
scanner.useDelimiter("\""); }
scanner.next(); }
String street = scanner.next(); }

Listing 4. Zawartość metody transformFromPlainToXmlFile wraz ze zdekomponowanym algorytmem

public void transformFromPlainToXmlFile(String try {


plainFileName, // 4.Przygotuj strumień do zapisu
String xmlFileName) { bufferedWriter = createWriter(xmlFileName);
// 1.Przygotuj strumień do czytania // 5.Zapisz dane w formacie XML
Scanner scanner = createScanner(plainFileName); writeToXml(bufferedWriter, persons);
// 2.Wczytaj dane o osobach } finally {
List<Person> persons = parseForPersons( scanner ); // 6.Zamknij strumień zapisu
// 3.Zamknij strumień odczytu closeWriter(bufferedWriter);
scanner.close(); }
BufferedWriter bufferedWriter = null; }

74 12/2009
Skromny programista

reprezentowane przez następujące wyli- Połączenie tych zasad daje metodę, któ- „najprostsze jak to tylko możliwe, ale nie
czenie: rej nie oprze się żaden kod. W ten sposób prostsze”, cytując Einsteina. Nie oznacza, że
upraszczamy kod. Gdy kod jest prosty, wte- pierwsze rozwiązanie, które przychodzi nam
enum Indent { dy jest czytelny. Kiedy jest czytelny, poświę- do głowy, spełnia to założenie. Jak mogliśmy
NONE, FIRST, SECOND; camy znacząco mniej czasu na jego tworze- się przekonać na powyższym przykładzie, to
} nie i zmiany. co wydaje się pozornie proste, bywa bardzo
trudne w utrzymaniu. Proste w tym przy-
Metoda writeToXml po zmianach będzie Podsumowanie padku oznacza przemyślane i łatwe w zrozu-
wyglądać jak na Listingu 6. Upraszczanie, Opisanym technikom towarzyszy im zasa- mieniu. Owa prostota wymaga od nas nieco
upraszczanie i jeszcze raz upraszczanie. Każ- da – to co powstaje w kodzie, niech będzie wysiłku, który jednak szybko się zwraca.
dy kod można uprościć używając tylko tych
trzech technik: MICHAŁ BARTYZEL, MARIUSZ SIERACZKIEWICZ
Trenerzy i konsultanci w firmie BNS IT. Badają i rozwijają metody psychologii programowania, poma-
• dekompozycji algorytmu; gające programistom lepiej wykonywać ich pracę. Na co dzień Autorzy zajmują się zwiększaniem efek-
• wyodrębniania metody; tywności programistów poprzez szkolenia, warsztaty oraz coaching i trening.
• usuwanie powtórzeń. Kontakt z autorami: m.bartyzel@bnsit.pl, m.sieraczkiewicz@bnsit.pl

Listing 5. Przykład wyodrębnionej metody writeToXml

private void writeToXml(BufferedWriter bufferedWriter, bufferedWriter.newLine();


List<Person> persons) { bufferedWriter.write(
try { " <ulica>" + person.getStreet() +
bufferedWriter.write("<?xml version=\"1.0\" "</ulica>");
encoding=\"UTF-8\"?>"); bufferedWriter.newLine();
bufferedWriter.newLine(); bufferedWriter.write(
bufferedWriter.write("<osoby>"); " <miasto>" + person.getCity() +
bufferedWriter.newLine(); "</miasto>");
for (Person person : persons) { bufferedWriter.newLine();
bufferedWriter.write(" <osoba>"); bufferedWriter.write(" <dataurodzenia>"
bufferedWriter.newLine(); + DateFormat.getDateInstance(DateFormat.FULL)
bufferedWriter.write( .format(person.getBirth())
" <imie>" + person.getName() + "</ + "</dataurodzenia>");
imie>"); bufferedWriter.newLine();
bufferedWriter.newLine(); bufferedWriter.write(" </osoba>");
bufferedWriter.write( bufferedWriter.newLine();
" <nazwisko>" + }
person.getSecondName() + "</ bufferedWriter.write("</osoby>");
nazwisko>"); bufferedWriter.newLine();
bufferedWriter.newLine(); } catch (IOException e) {
bufferedWriter.write( throw new ApplicationException(e);
" <pesel>" + person.getPesel() + }
"</pesel>"); }

Listing 6. Metoda writeToXml po refaktoryzacji

private void writeToXml( Indent.SECOND);


BufferedWriter bufferedWriter, List<Person> persons) { writeLine(bufferedWriter, "<ulica>" +
writeLine(bufferedWriter, person.getStreet() + "</ulica>",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>", Indent.SECOND);
Indent.NONE); writeLine(bufferedWriter, "<miasto>" +
writeLine(bufferedWriter, "<osoby>", Indent.NONE); person.getCity() + "</miasto>",
for (Person person : persons) { Indent.SECOND);
writeLine(bufferedWriter, "<osoba>", Indent.FIRST); writeLine(bufferedWriter, "<dataurodzenia>"
writeLine(bufferedWriter, "<imie>" + person.getName() + DateFormat.getDateInstance(DateFormat.FULL).
+ "</imie>", format(
Indent.SECOND); person.getBirth()) + "</dataurodzenia>",
writeLine(bufferedWriter, "<nazwisko>" + Indent.SECOND);
person.getSecondName() writeLine(bufferedWriter, "</osoba>", Indent.FIRST);
+ "</nazwisko>", Indent.SECOND); }
writeLine(bufferedWriter, "<pesel>" + writeLine(bufferedWriter, "</osoby>", Indent.NONE);
person.getPesel() + "</pesel>", }

www.sdjournal.org 75
Akademia UML

Liczebności klas
uczestniczących w powiązaniach

Określenie liczebności klas biorących udział w powiązaniach na


diagramach klas to jedna z najważniejszych decyzji analitycznych,
mająca niebagatelny wpływ na funkcjonalność modelowanego
systemu. Zobaczmy więc, jak poprawnie określać liczebności, aby
uniknąć przykrych niespodzianek.
nie nasz pracownik postąpi z pojazdami. Ta-
Dowiesz się: Powinieneś wiedzieć: ki sposób pracy z systemem byłby bardzo
• Jak poprawnie określać liczebności klas bio- • Znać notację diagramów klas języka UML; obiecujący, ponieważ pracownicy nie musie-
rących udział w powiązaniach; • Znać podstawowe zasady analizy i projekto- liby ponownie wklepywać danych poszkodo-
• Co to są globalne i lokalne rejestry obiek- wania obiektowego. wanych oraz pojazdów, którzy są juz zapisa-
tów. ni w bazie.
Przyjrzyjmy się jednak, jakie będą konse-
kwencje stosowania takiego modelu w dłuż-
szym okresie czasu. Wyobraźmy sobie, że
my ubezpieczeniowej, tzn. ma w niej wy- pani Janina Kowalska była poszkodowana
kupioną polisę OC. Dlatego szkodę przy- w wypadku w lipcu 2007 roku. Wówczas
Poziom porządkowaliśmy bezpośrednio do po- w systemie zarejestrowano zgłoszoną przez
trudności lisy, z której będzie wypłacone odszko- nią szkodę i wprowadzono jej dane do ba-
dowanie. Natomiast osoba poszkodowa- zy poszkodowanych. Kilka miesięcy póź-
na zwykle klientem naszej firmy nie jest. niej wyszła za mąż i zmieniła nazwisko – te-
Dlatego zamodelowaliśmy ją w posta- raz nazywa się Janina Nowak. Pech chciał, że

Z
ałóżmy, że projektujemy system dla ci osobnej klasy Poszkodowany. Na tej sa- w grudniu 2009 roku ponownie brała udział
firmy ubezpieczeniowej, która ofe- mej zasadzie odróżniliśmy pojazdy spraw- w wypadku, którego sprawcą był klient tej
ruje m. in. obowiązkowe ubezpie- ców ubezpieczone w naszej firmie (klasa samej firmy ubezpieczeniowej. Zgłasza więc
czenia odpowiedzialności cywilnej (OC) po- PojazdUbezpieczony) od pojazdów uczest- kolejną szkodę. Pracownik rejestrujący szko-
jazdów. Nasz system będzie pozwalał na re- ników zdarzenia (klasa Pojazd ). dę wyszukuje jej dane, wprowadzając do wy-
jestrowanie i obsługę szkód wyrządzonych Naturalne jest, że jedna osoba – o ile jest szukiwarki numer PESEL. Okazuje się, że
przez klientów naszej firmy innym uczestni- wystarczająco pechowa – może być poszko- pani Janina figuruje już w bazie poszkodo-
kom ruchu drogowego. Trzeba zatem stwo- dowana w wielu szkodach. Dlatego w na- wanych, lecz pod starym nazwiskiem. Nie-
rzyć model, zgodnie z którym w systemie re- szym modelu przy powiązaniu klas Szkoda wiele się zastanawiając, nasz pracownik ak-
jestrowane i przechowywane będą informa- i Poszkodowany po stronie klasy Szkoda po- tualizuje dane pani Janiny i rejestruje no-
cje o szkodach. jawia się liczebność 0..*. W podobny spo- wą szkodę.
Podczas sesji analitycznej dowiadujemy sób dopuszczamy, aby jeden pojazd mógł Załóżmy teraz, że z jakichś powodów na-
się, że pracownicy zajmujący się likwidacją brać udział w wielu szkodach. Efektem te- sza firma ubezpieczeniowa wraca do spra-
szkód będą do systemu wprowadzali zgło- go będzie powstanie globalnej, wspólnej dla wy wypadku pani Janiny sprzed dwóch
szenia szkód. Każde takie zgłoszenie będzie wszystkich szkód bazy poszkodowanych oraz i pół roku. I co się wtedy okazuje? Że w sys-
zawierać: globalnej bazy pojazdów uczestniczących w temie zarejestrowano jako poszkodowaną
zdarzeniach. panią Janinę Nowak, mimo że na papiero-
• dane osoby poszkodowanej; Zastanówmy się, jak będzie działał system wym formularzu zgłoszeniowym, zeskano-
• dane sprawcy; zgodny z takim modelem. Otóż pracownik wanym i przechowywanym w archiwum,
• dane pojazdów biorących udział w zda- rejestrujący zgłoszenie szkody będzie mu- podpisała się jako Janina Kowalska! Podob-
rzeniu (w tym pojazdu sprawcy i osoby siał najpierw sprawdzić, czy poszkodowany ną sytuację mielibyśmy w przypadku zmia-
poszkodowanej). był już wcześniej poszkodowanym w jakimś ny danych pojazdu uczestniczącego w kil-
innym zdarzeniu i czy jest już zarejestrowa- ku szkodach.
Model systemu rejestracji szkód obej- ny w systemie. W tym celu wykona zapew- Jak więc widać, nasz model dopuszcza
mujący te informacje jest przedstawio- ne wyszukiwanie w bazie poszkodowanych. dokonywanie zmian w danych historycz-
ny na Rysunku 1. W modelu tym zakła- Dopiero jeśli nie znajdzie w niej właściwej nych przy okazji wprowadzania nowych
damy, że sprawca jest klientem naszej fir- osoby, wprowadzi jej dane do bazy. Podob- danych. Wynika to z tego, że obiekty kla-

76 12/2009
Liczebności klas

sy Poszkodowany i Pojazd mogą być współ- wadzone przez kogo innego dwa i pół roku systemu przy każdej szkodzie. Koncepcję
dzielone przez kilka szkód. Sytuacja taka później. tę ilustruje model z Rysunku 2. Użyliśmy
– jak można się domyślić – nie jest pożąda- Okazuje się więc, że globalna baza w nim kompozycji, aby zaznaczyć, że dane
na, zwłaszcza jeśli weźmiemy pod uwagę, wszystkich poszkodowanych oraz pojaz- poszkodowanych i pojazdów przynależą do
że likwidacją szkód w naszej firmie ubez- dów, zaproponowana w modelu z Rysunku konkretnych szkód.
pieczeniowej zajmuje się kilkaset osób roz- 1, nie jest dobrym rozwiązaniem. Właści- Co jednak zrobimy, jeśli ta sama osoba
sianych po całym kraju. Pracownik obsłu- we byłoby raczej uniemożliwienie współ- zgłosi się do nas kolejny raz jako poszkodo-
gujący pierwszą szkodę pani Janiny odpo- dzielenia tych danych przez wiele szkód. wana? Skoro model zabrania nam przypi-
wiada za jakość swojej pracy i nie może brać Oznacza to, że dane poszkodowanych oraz sać jej dane do kolejnej szkody, to po prostu
odpowiedzialności za zmiany danych wpro- ich pojazdów muszą być wprowadzane do wprowadzimy jej dane ponownie. Dane bę-
dą więc powtórzone, ale dzięki temu każda
szkoda będzie miała swój prywatny komplet
danych, które nie będą współdzielone z in-
nymi szkodami. Zmiany danych poszkodo-
������������������ wanego lub pojazdu, dokonane już po ob-
�������� ������ służeniu szkody (np. przy rejestracji kolej-
�����������������������������
���������������������� ������������������� ���������������������� nej szkody), nie będą więc odzwierciedla-
������������������ ���� � ���� � ������������������
������������������� ne w danych historycznych dotyczących tej
������������������ ������������������� �������������������

szkody.
�����������������������
Okazuje się więc, że dokładne odzwier-
�������
ciedlanie rzeczywistości biznesowej nie
���� zawsze przynosi dobre rezultaty. Rzeczy-
������
wistość firmy ubezpieczeniowej jest taka,
������
że jednej osobie może się zdarzyć kilku-
����������������������� �����������������������������
������������������������� ���� ���� ������������������� krotne uczestnictwo w wypadku. Tę rze-
��������������������������� ������������������� czywistość odzwierciedla model z Rysun-
���� ku 1. Jednak tworząc model, trzeba także
uwzględnić uwarunkowania organizacyjne
���� oraz oczekiwania odnośnie funkcjonalno-
������������
ści systemu. Może to spowodować zmianę
modelu i przyjęcie pewnych – na pierwszy
����������������������
������������������ rzut oka niepoprawnych – założeń, tak jak
������������������� w modelu z Rysunku 2.
Na koniec zwróćmy uwagę, że powyższe
rozważania nie dotyczą sprawców szkód,
ich pojazdów oraz polis. Sprawcy są bo-
Rysunek 1. Globalna baza poszkodowanych i pojazdów
wiem klientami naszej firmy ubezpiecze-
niowej. Musi więc ona przechowywać ich
pełną bazę, wraz z danymi ich pojazdów
i polis. Klient jest w tej bazie przechowywa-
ny także wtedy, gdy nie był sprawcą żadnej
������������������ szkody. Jego dane są w niej aktualizowane,
�������� ������
����������������������������� jeśli tylko złoży zgłoszenie zmiany danych.
���������������������� ����������������������
������������������ ���� �
�������������������
���� � ������������������ Innymi słowy, baza klientów, pojazdów
�������������������
������������������ ������������������� ������������������� i polis istnieje niezależnie od systemu ob-
� ����������������������� sługi szkód. Nic nie stoi więc na przeszko-
������� dzie, abyśmy z niej podczas rejestrowania
szkód korzystali.
����
������ ������
����������������������� �����������������������������
������������������������� � ���� �������������������
��������������������������� ������������������� SZYMON ZIOŁO
� Główny konsultant w firmie RedPill, oferującej
specjalistyczne szkolenia informatyczne oraz
���� doradztwo w zakresie zarządzania projekta-
������������ mi, modelowania procesów biznesowych oraz
���������������������� analizy i projektowania systemów informatycz-
������������������ nych. Analityk biznesowy i systemowy, specjali-
������������������� zujący się w analizie obiektowej, modelowaniu
w języku UML oraz w XML-u i technologiach po-
krewnych.
Rysunek 2. Dane poszkodowanych i pojazdów przypisane do szkody Kontakt z autorem: sziolo@redpill.com.pl

www.sdjournal.org 77
KLUB PRO
Oferta skierowana dla firm
Jeżeli Twoja firma jest prenumeratorem Software Developer’s Journal za comiesięczną
dopłatą 50 PLN +VAT możesz dodatkowo otrzymać reklamę.
Wystarczy tylko, aby profil Twojej firmy pokrywał się z naszym magazynem.
Wyślij do nas: logo firmy, dane kontaktowe i informację o firmie
Reklama przez 12 kolejnych numerów tylko za 600 PLN +VAT.
Jeżeli nie posiadasz jeszcze prenumeraty możesz ją zamówić w atrakcyjnej cenie.
Dla nowych prenumeratorów specjalna oferta – 690 PLN.

Skontaktuj się z nami:


tel. +48 22 877 20 80
fax: +48 22 877 20 70
software@europress.pl

Opera Software Architektury systemów IT


Opera Software’s vision is to deliver the best In- Twórca frameworków JUVE i serwera aplikacji
ternet experience on any device. We are offering AVAX oferuje usługi, doradztwo, rozwiązania do
browser for PC/desktops and embedded pro- tworzenia nowoczesnych, dużych systemów i roz-
ducts that operates across devices, platforms wiązań informatycznych/internetowych, integrujące
and operating systems. Our browser can deliver architektury ery post-J2EE/.NET, wykorzystujące
a faster, more stable and flexible Internet expe- MDD/MDA dla dziedzin – bankowość, telekomuni-
rience than its competitors. kacja, handel, e-commerce, ERP/Workflow/CRM,
rozwiązania internetowe, portalowe.
http://www.opera.com www.mpsystem.com mpsystem@mpsystem.com

Kei.pl Future Processing


Kei.pl działa na rynku usług hostingowych od 2000 Future Processing to dynamiczna firma technolo-
roku. Do naszych zadowolonych Klientów z du- giczna działająca na globalnym rynku oprogramo-
mą możemy zaliczyć wiele przedsiębiorstw sekto- wania. Jesteśmy zespołem wysokiej klasy specja-
ra MSP, instytucji oraz osób prywatnych. W ofer- listów posiadających wiedzę i doświadczenie nie-
cie Kei.pl znajdują się pakiety hostingowe, a także zbędne do realizacji ambitnych projektów informa-
usługi dla wymagających Użytkowników – platfor- tycznych. Jeśli programowanie to Twoja pasja do-
my e-Biznes oraz serwery fizyczne. łącz do nas! (możliwość pracy zdalnej).

http://www.kei.pl http://www.future-processing.pl

Playsoft WSISiZ w Warszawie


Playsoft jako lider portowania aplikacji na plat- INFORMATYKA ZARZĄDZANIE
formy mobilne wciąż powiększa bazę swo- studia stopnia I i II (stacjonarne i niestacjonar-
ich klientów: EA Mobile, Sega, THQ, Kona- ne) specjalności: inżynierskie, magisterskie
mi. W ramach rozszerzania swojej działalno- i licencjackie. Szczegółowe plany studiów, opi-
ści, poszukujemy doświadczonego programi- sy poszczególnych specjalności – zapraszamy
sty, który byłby odpowiedzialny za tworzenie na stronę uczelni.
aplikacji na platformy Iphone, Windows Mobi-
le, Android.
http://www.playsoft.fr http://www.wit.edu.pl
KLUB PRO

INFOTEX SP.J TTS Company Sp. z o.o.


Śmietanowski i Wsp. Sprzedaż i dystrybucja oprogramowania komputero-
Dystrybutor XP Unlimited – Serwer Terminali dla wego. Import programów na zamówienie. Ponad 200
Windows XP i VISTA. Program umożliwia łącze- producentów w standardowej ofercie. Chcesz kupić
nie się z dowolnego klienta Windows, Linux z wy- oprogramowanie i nie możesz znaleźć polskiego do-
korzystaniem protokołu RDP. Cena wersji Classic stawcy? Skontaktuj się z nami – sprowadzimy nawet
dla 5 użytkowników - 165€, dla nieograniczonej
liczby - 235€. Ponadto oferujemy opiekę serwiso- pojedyncze licencje.
wą i aplikacje internetowe na zamówienie.

http://www.infotex.com.pl http://www.OprogramowanieKomputerowe.pl

IT SOLUTIONS Softline rozwiązania mobilne


Wdrożenia i szkolenia z zakresu: Wiodący producent systemów mobilnych, do-
• SQL Server stawca aplikacji użytkowych dla biznesu (Sym-
• SharePoint Services bian OS, Windows Mobile, J2ME ) zaprasza do
IT SOLUTIONS • MS Project / Server
• Tworzenie aplikacji w technologii .NET
współpracy. Zostań naszym partnerem. Dołącz
do zespołu.

http://www.itsolutions.biz.pl
marcin.pytlik@itsolutions.biz.pl http://www.softline.com.pl

Proximetry Poland Sp. z o.o. Systemy bankowe, ISOF


Proximetry Poland Sp. z o.o. jest polskim od- HEUTHES istnieje na rynku od 1989 r. Obok
działem amerykańskiej firmy Proximetry Inc. – systemów informatycznych dla banków, ofe-
dostawcy systemów zarządzania sieciami bez- ruje nowoczesne oprogramowanie do obsługi
przewodowymi opartymi na technologiach WiFi
i WiMAX. Naszą misją jest dostarczenie klien- firm. System ISOF jest udostępniany klientom
tom rozwiązań poprawiających jakość usług w trybie SaaS lub licencji. Pracuje na platfor-
(QoS) dostarczanych drogą radiową. Dołącz do mie Linux i zawiera m.in. takie moduły jak
najlepszych i zostań członkiem naszej ekipy! CRM, DMS, Magazyn, Sprzedaż, Logistyka
oraz Rachunkowość.
http://www.proximetry.com http://www.isof.pl
Roczna prenumerata

tylko
259,-
Software Developer’s Journal (poprzednio Software 2.0)
jest miesięcznikiem głównie dla programistów, którzy li-
czą, że dostarczymy im gotowe rozwiązania, oszczędza-
jąc im czasu i pracy. Jesteśmy czytani przez tych, któ-
rzy chcą być na bieżąco informowani o najnowszych osią-
gnięciach w dziedzinie IT i nie chcą, żeby jakiekolwiek
istotne wydarzenia umknęły ich uwadze. Aby zadowolić

UWAGA!
naszych czytelników, prezentujemy zarówno najnowsze
rozwiązania, jaki starsze, sprawdzone technologie.

Zmiana danych
kontaktowych

Obsługa prenumeraty
1. Telefon 3. Adres
+48 22 877 20 80 EuroPress Polska Sp. z o.o.
2. Fax ul. Jana Kazimierza 46/54
+48 22 877 20 70
01-248 Warszawa
2. Online
software@europress.pl
Zamówienie prenumeraty

Prosimy wypełniać czytelnie i przesyłać faksem na numer:


00 48 22 877 20 70
lub listownie na adres:
EuroPress Polska Sp. z o.o.
ul. Jana Kazimierza 46/54
01-248 Warszawa
Polska
E-Mail: software@europress.pl

Przyjmujemy też zamównienia telefoniczne:


00 48 22 877 20 80

Jeżeli chcesz się dowiedzieć o formach płatności, wejdź na stronę:


www.europress.pl lub napisz na e-mail: software@europress.pl

Imię i nazwisko ...............................................................................

Nazwa firmy.....................................................................................

Dokładny adres ..............................................................................

.........................................................................................................

Zadzwoń Telefon ............................................................................................

0
+48 22 877 20 8
E–mail .............................................................................................

lub ID kontrahenta ................................................................................

zamów Numer NIP firmy .............................................................................

mailowo! Fax (wraz z nr kierunkowym) .........................................................

□ automatyczne przedłużenie prenumeraty

Prenumerujesz
– zyskujesz
l oszczędność
pieniędzy Ilość Od
l szybka dostawa Tytuł
Ilość
nume-
zama- numeru
wianych pisma Cena
l prezenty rów prenu- lub mie-
merat siąca
l bezpieczna płatność Software Developer’s
Journal (1 płyta CD) 12 259
on–line – dawniej Software 2.0 PLN

You might also like