Download pdf - SDJ 08 2010

Transcript
Page 1: SDJ 08 2010
Page 2: SDJ 08 2010

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Page 3: SDJ 08 2010

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Page 4: SDJ 08 2010

4

SPIS TREŚCI

BIBLIOTEKA MIESIĄCA6 Google SparseHash – Wyspecjalizowane kontenery haszujące w języku C++Rafał KociszStandardowa Biblioteka Wzorców (STL) języka C++ to wspaniałe i potężne narzędzie. Niestety – ewident-nie doskwiera w niej brak wsparcia dla kontenerów mieszających. Nowy standard C++ ma rozwiązać ten problem, ale póki co trzeba szukać innych alternatyw w postaci zewnętrznych bibliotek. W niniejszym arty-kule przedstawię implementację tablic haszujących ro-dem z Google.

KLUB TECHNICZNY16 Technologie Progress OpenEdge – Część 9. OpenEdge SQLPiotr TucholskiOpenEdge SQL stworzony przez Progress Software Corporation jest implementacją powszechnie znanych standardów, włączając SQL-92, SQL-99 i SQL-2003. Jest częścią otwartego, elastycznego interfejsu i peł-ni niezwykle ważną rolę w procesie rozwoju nowocze-snych aplikacji biznesowych OpenEdge.

PROGRAMOWANIE PYTHON20 Kurs Pythona. Cz.II – Struktury danych, funkcje i modułyŁukasz LangaW odcinku wprowadzającym zainstalowaliśmy Pythona i trochę pobawiliśmy się różnymi jego cechami. Po na-braniu swobody w wykorzystaniu linii poleceń możemy zabrać się za bardziej metodyczny przegląd tego, co oferuje nam język spod znaku węża.

8/2010 (188)

4 8/2010

PROGRAMOWANIE JAVA30 Przewodnik po SCJP – Czyli certyfikat z Javy cz.VKrzysztof Rychlicki - KiciorProces 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 pro-gramistów (Java lub .NET), administratorów czy sie-ciowców (Cisco) można coraz częściej odnaleźć w CV osób starających się o pracę.

36 Java na BlackBerry – Podstawy pisania aplikacjiTomasz MilczarekArtykuł przedstawia podstawy programowania aplika-cji w języku Java pod system BlackBerry. W podstawo-wym zakresie omówione zostały cztery tematy:ogólne sposoby tworzenia aplikacji, budowanie interfejsu użyt-kownika, programowanie menu telefonu oraz zagad-nienie utrwalania danych.

42 Wiosna z drugą twarzą w chmurach – Część IPaweł NieściorukKompletny przykład procesu wytwarzania aplikacji Ja-va Server Faces 2.0 z użyciem Spring Framework i Hi-bernate wraz z jej końcowym wdrożeniem na chmurę obliczeniową Amazon Elastic Compute Cloud (EC2).

NIEZAWODNE OPROGRAMOWANIE50 Testowanie gier na urządzenia mobilneGrzegorz TarczyńskiTestowanie gier jest niezwykle złożoną materią, zwłaszcza przy dużych, wysokobudżetowych pro-dukcjach. Gry na urządzenia mobilne zwykle do ta-kich nie należą, co nie oznacza, że proces testowa-nia jest tu mało istotny czy możliwy do pominięcia. Ja-ka jest specyfika tego procesu, dowiecie się z niniej-szego artykułu.

Page 5: SDJ 08 2010

4

Miesięcznik Software Developer’s Journal (12 numerów w roku)

jest wydawany przez Software Press Sp. z o.o. SK

Redaktor naczelny: Łukasz Łopuszański [email protected]

Projekt okładki: Agnieszka Marchocka

Skład i łamanie: Tomasz Kostro www.studiopoligraficzne.com

Kierownik produkcji: Andrzej Kuca [email protected]

Adres korespondencyjny:Software Press Sp. z o.o. SK,

ul. Bokserska 1, 02-682 Warszawa, Polskatel. +48 22 427 36 91, fax +48 22 224 24 59

www.sdjournal.org [email protected]

Dział reklamy: [email protected] 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.

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

Zostały użyte wyłącznie w celach informacyjnych.

Osoby zainteresowane współpracą prosimy o kontakt:[email protected]

SPIS TREŚCI

Reklama

Z ŻYCIA ITOLOGA 58 CMMI – Dlaczego powinno Cię to obchodzić?Mariusz ChrapkoPamiętam jak zaczynałem swoją przygodę z rozwiązy-waniem sudoku. Na początku było niemiłosiernie trud-no, potem stopniowo łapałem „wiatr w żagle”. Podob-nie jest z modelem CMMI. Na pierwszy rzut oka wy-daje się bardzo skomplikowany. Później, w miarę jak stopniowo go poznajemy, zaczynamy dostrzegać jego wewnętrzne „piękno” i logikę, widzimy że jego prakty-ki naprawdę mają sens i mogą nam się przydać. Tak było ze mną, i tak – jestem o tym przekonany – będzie również z Wami!

WYWIAD64 Mariusz Chrapko – Wywiad z autorem pierwszej w Polsce książki na temat modelu CMMI oraz jego praktycznego zastosowania.Mariusz Chrapko jest wieloletnim praktykiem w zakre-sie doskonalenia procesów tworzenia oprogramowa-nia w oparciu o model CMMI®. Wspiera firmy informa-tyczne na terenie całej Europy, prowadząc coaching zespołów projektowych oraz szkolenia na różnych szczeblach organizacyjnych. Dodatkowo jest prakty-kiem we wdrażaniu i adaptacji metod Agile Software Development (wcześniej Agile Coach/ Centrum Opro-gramowania Motoroli w Krakowie), Programu Metryk Organizacyjnych, a także procesu Peer Review.

SPIS TREŚCI

Page 6: SDJ 08 2010

8/20106

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 7

Struktury danych to niewątpliwie jeden z naj-bardziej elementarnych, a zarazem kluczo-wych komponentów z których zbudowane są

nasze programy. Posiadanie wydajnych struktur da-nych to jeden z wielu kluczy do osiągnięcia sukcesu w inżynierii oprogramowania. Dziś, dzięki bibliotekom standardowym, które wyposażone są w cały szereg przydatnych, a zarazem łatwych w użyciu i – co waż-ne – bardzo wydajnych kontenerów, problem nisko-poziomowych struktur danych w pewnym stopniu zo-stał usunięty ze świadomości programistów. Nie wie-rzysz? Więc zapytaj sam siebie: kiedy implemento-wałeś ostatnio własny, wyspecjalizowany kontener? A w następnej kolejności zadaj takie samo pytanie kilku swoim znajomym programistom...

Wysokiej jakości biblioteka standardowa to oczywi-ście Bardzo Dobra Rzecz. Dzięki takim bibliotekom jak STL czy Java Collection Framework, lista czy al-gorytm sortowania stały się podstawowymi klocka-mi, z których mogę niejako składać swoje programy. Dzięki temu jestem w stanie skupić się bardziej na ich właściwej funkcjonalności. Innym słowy: tworząc mo-je aplikacje mogę myśleć na wyższym poziomie abs-trakcji. W zasadzie powinienem być zadowolony...

Jednakże, operowanie na wysokim poziomie abs-trakcji ma zarówno swoje dobre jak i złe strony. Ne-gatywnym czynnikiem może być w tym przypad-ku zjawisko polegające na tym, że tworząc progra-my w ten sposób przestajemy dostrzegać szczegóły.

W językach wysokiego poziomu (Perl, Python, Ruby itd...) jest to efekt jak najbardziej pożądany. Jednakże w językach niższego poziomu (np. C, bądź C++, któ-ry dziś de facto jest językiem zdecydowanie niskopo-ziomowym) efekt taki może być w wielu przypadkach nie do zaakceptowania.

Ciekawy casus stanowi wspomniana wcześniej bi-blioteka STL (a w zasadzie część tej biblioteki wcho-dząca w skład standardu języka C++). Przez wie-lu (również przez autora niniejszego tekstu) uwa-żana za arcydzieło inżynierii oprogramowania, po-siada niestety pewne wady. Jedną z nich jest brak kontenerów zbudowanych w oparciu o tablice mie-szające (ang. hash tables). Co ciekawe – tablice te nie zostały wprowadzone do biblioteki standardowej C++ z bardzo prozaicznego, czysto ludzkiego powo-du: Komisji opracowującej standard C++98 po pro-stu zabrakło czasu. W efekcie, cała rzesza programi-stów języka C++ wykorzystuje na co dzień kontene-ry asocjacyjne oparte na zrównoważonych drzewach. Co gorsza, młodsi, tudzież słabiej wyedukowani pro-gramiści mogą wręcz nie zdawać sobie sprawy z ist-nienia rozwiązań alternatywnych, często o wiele le-piej dostosowanych do wymagań naszych progra-mów. W tym kontekście można powiedzieć, że bi-blioteka standardowa języka C++ w pewnym sensie ogranicza nasze umysły... Cóż – jest to niewątpliwie cena postępu. W końcu po to budujemy kolejne war-stwy abstrakcji aby nie zaprzątać sobie głowy zbęd-

Google SparseHash

Standardowa Biblioteka Wzorców (STL) języka C++ to wspaniałe i potężne narzędzie. Niestety – ewidentnie doskwiera w niej brak wsparcia dla kontenerów mieszających. Nowy standard C++ ma rozwiązać ten problem, ale póki co trzeba szukać innych alternatyw w postaci zewnętrznych bibliotek. W niniejszym artykule przedstawię implementację tablic haszujących rodem z Google.

Dowiesz się:• Do czego służy biblioteka Google SparseHash;• Jak rozpocząć z nią pracę;• Jak zastosować ją w praktyce;• Jak Google SparseHash integruje się z biblioteką STL.

Powinieneś wiedzieć:• Solidna znajomość języka C++;• Solidna znajomość biblioteki STL.

Wyspecjalizowane kontenery haszujące w języku C++

Page 7: SDJ 08 2010

8/20106

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 7

Jak łatwo się domyśleć, kontenery typu * _ hash _ map oferują funkcjonalność zbliżoną do standardowych kontenerów std::map z biblioteki STL. Z kolei kolek-cje typu * _ hash _ set to klasy bliźniacze dla STL-owego zbioru (std::set). Przedrostki w nazwach klas określają ich charakterystyki wydajnościowe. Konte-nery typu sparse (w języku polskim: rzadkie) zosta-ły zaprojektowane w taki sposób aby uczynić je bar-dzo wydajnymi pod kątem zużycia pamięci. Z kolei komponenty typu dense (w języku polskim: gęste) są stworzone z myślą o wysokiej wydajności w kontek-ście czasu trwania ich operacji.

Ostatni z wymienionych kontenerów (sparsetable), to tablica, która gwarantuje bezpośredni dostęp (w no-menklaturze nazewniczej biblioteki STL: random ac-cess) do elementów, a także operacje wstawiania i usu-wania elementów, a wszystko to w stałym czasie!

W kolejnych podpunktach niniejszego artykułu omówimy po kolei wyżej wymienione komponenty.

Pierwsze spojrzenieZanim przejdziemy do omówienia szczegółów zwią-zanych z biblioteką Google SparseHash, na roz-grzewkę rozważymy prosty przykład jej wykorzysta-nia. Konfiguracja Google SparseHash jest bardzo prosta (patrz: ramka Szybki Start). Spójrzmy na Li-sting 1, gdzie przedstawiony jest prosty przypadek użycia biblioteki (uwaga: wspomniany przykład nale-ży skompilować przy pomocy narzędzia Microsoft Vi-sual C++; więcej informacji na temat tego ogranicze-nia znajdziesz w dalszej części artykułu).

Czytelnikom, którzy znają podstawy biblioteki STL przedstawiony wyżej przykład powinien wydać się bar-dzo swojski. W zasadzie, poza definicją typu kontene-ra, cała reszta kodu wygląda tak, jak byśmy używali standardowego kontenera std::map. Proponuję drogi Czytelniku, abyś spróbował skompilować ten przykład zanim będziesz kontynuował dalsze czytanie... Udało się? Jeśli tak, to jedziemy dalej!

SparseHash: kontenery asocjacyjneW nomenklaturze biblioteki STL kontener asocja-

nymi szczegółami. Pozostaje tylko pytanie: czy aby zawsze zbędnymi...?

Wiele znanych i cenionych autorytetów z dziedziny programowania w języku C++ (między innymi Scott Meyers) wskazuje na to, że standardowe kontene-ry asocjacyjne pozostawiają wiele do życzenia pod względem wydajności oraz fragmentacji pamięci. Są za to bardzo uniwersalne. Jednakże nie zawsze tej uniwersalności potrzebujemy. A jeśli już decydujemy się pisać aplikację w stosunkowo trudnym języku ni-skopoziomowym, to zapewne zależy nam na wydaj-ności.

Cóż więc zrobić?

Google SparseHash przybywa na ratunek!Odpowiedzi na zadane wyżej pytanie jest klika. Moż-na oczywiście napisać własny, wyspecjalizowany kontener. Jednakże zanim zdecydujesz się na ten desperacki krok, pamiętaj że pisanie bezbłędnych, przenośnych i wydajnych kontenerów STL jest za-daniem niebanalnym. Inna alternatywa to wykorzy-stanie gotowej biblioteki napisanej przez kogoś inne-go (najlepiej eksperta z danej dziedziny). Ja chciał-bym zainteresować Czytelników niniejszego tekstu do zwrócenia uwagi na Google SparseHash.

Dlaczego akurat ta biblioteka? Po pierwsze, oferuje ona zestaw kontenerów haszujących o różnych cha-rakterystykach wydajnościowych. Po drugie, kompo-nenty dostarczone w ramach SparseHash spełniają wymagania nakładane na kontenery w ramach spe-cyfikacji STL, dzięki czemu możemy swobodnie wy-korzystywać razem tą biblioteką tudzież z innymi bi-bliotekami zgodnymi z STL (np. Boost).

Google SparseHash w wersji 1.7 (najnowsza, sta-bilna wersja biblioteki dostępna w momencie pisania niniejszego artylułu) oferuje nam pięć kontenerów:

• sparse _ hash _ map,• sparse _ hash _ set,• dense _ hash _ map,• dense _ hash _ set,• sparsetable.

Szybki startGoogle SparseHash jest w całości zaimplementowana w plikach nagłówkowych i nie wymagane jest jej budowanie przed uży-ciem, tudzież dołączanie do projektu, w którym chcielibyśmy jej użyć, dodatkowych bibliotek w postaci binarnej. Jedyne co na-leży zrobić to poinstruować kompilator gdzie ma szukać nagłówków.W tym celu należy wskazać kompilatorowi katalog SPARSEHASH_HOME/src (zakładamy, że SPARSEHASH_HOME to ścieżka wska-zująca na domowy katalog biblioteki). W przypadku gdy pracujemy pod systemem operacyjnym z rodziny Microsoft Windows, dodatkowo musimy wskazać podkatalog SPARSEHASH_HOME/src/windows.Po wykonaniu wspomnianych kroków biblioteka jest gotowa do użycia. Zanim jednak zaczniemy pracę, warto jeszcze poświęcić kilka chwil i uruchomić zestaw testów dołączonych do biblioteki. Instrukcje opisujące jak to zrealizować te zadanie znajdują się w plikach SPARSEHASH_HOME/README oraz SPARSEHASH_HOME/README.windows. Po tym jak wszystkie testy zostaną pomyślnie ukończone możemy z czystym sumieniem zabrać się do pracy!

Page 8: SDJ 08 2010

8/20108

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 9

cyjny to kolekcja, która przechowuje pary powiąza-nych ze sobą elementów: kluczy (ang. keys) i warto-ści (ang. values). Głównym celem tworzenia i używa-nia kontenerów asocjacyjnych jest możliwość szyb-kiego wyszukiwania wartości na podstawie zadanych kluczy. Podręcznikowy przykład wykorzystania kon-tenera asocjacyjnego to budowanie histogramu wy-stępowania wyrazów w zadanym tekście. Zadanie to można rozwiązać bardzo prosto korzystając ze stan-dardowego kontenera std::map z biblioteki STL. Roz-wiązanie takie pokazano na Listingu 2.

Kontener asocjacyjny std::map, jak wszystkie ko-lekcje rodem z biblioteki STL, zaimplementowany

jest w postaci szablonu klasy. Szablon ten przyjmu-je dwa parametry: typ klucza oraz typ wartości (tak naprawdę wspomnianych parametrów jest nieco wię-cej, ale część z nich jest domyślna i nie będziemy so-bie teraz zawracali nimi głowy). W naszym przykła-dzie kluczami są napisy (stąd typ std::string jako pierwszy argument szablonu), zaś wartościami – licz-by całkowite (int), które pełnią rolę liczników wystą-pień poszczególnych słów.

W przypadku gdybyśmy chcieli zbudować histo-gram występowania wyrazów dla większego tekstu, kluczową staje się kwestia wydajności operacji wy-szukiwania wartości na podstawie klucza. W prak-tyce kontenery asocjacyjne powinny gwarantować, że taka operacja ma maksymalną złożoność algo-rytmiczną klasy O(log n) (to znaczy, że przy wystę-powaniu n elementów w kontenerze, czas potrzebny do wyszukania zadanego elementu będzie proporcjo-nalny do wartości log n). Z tego względu, kontenery asocjacyjne opierają się zazwyczaj na wyspecjalizo-wanych strukturach danych, które taką właśnie zło-

Listing 1. Prosty przypadek użycia biblioteki SparseHash

#include <cstring>

#include <iostream>

#include <hash_map>

#include <google/sparse_hash_map>

using namespace google;

using namespace std;

using namespace stdext; // for hash_compare

int main()

{

sparse_hash_map<const char*, int, hash_compare<const

char*> > months;

months["january"] = 31;

months["february"] = 28;

months["march"] = 31;

months["april"] = 30;

months["may"] = 31;

months["june"] = 30;

months["july"] = 31;

months["august"] = 31;

months["september"] = 30;

months["october"] = 31;

months["november"] = 30;

months["december"] = 31;

cout << "may -> " << months["may"] << endl;

cout << "september -> " << months["september"] <<

endl;

cout << "april -> " << months["april"] << endl;

cout << "june -> " << months["june"] << endl;

cout << "november -> " << months["november"] <<

endl;

return 0;

}

Listing 2. Zliczanie wystąpień wyrazów w tekście przy pomocy kontenera std::map

#include <iostream>

#include <map>

#include <string>

#include <boost/foreach.hpp>

#define foreach BOOST_FOREACH

using namespace std;

int main()

{

string word;

map<string, int> histogram;

while (cin >> word)

{

histogram[word]++;

}

typedef map<string, int>::value_type histogram_

entry;

foreach (const histogram_entry& entry, histogram)

{

cout << entry.first << " -> " << entry.second

<< endl;

}

}

Page 9: SDJ 08 2010

8/20108

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 9

żoność operacji wyszukiwania gwarantują. Kolekcja std::map jest zazwyczaj implementowana jako zrów-noważone drzewo wyszukiwania.

Jak to w życiu bywa: kontener kontenerowi nierów-ny. Zrównoważone drzewo wyszukiwania jest bar-dzo uniwersalną strukturą danych. W praktyce ozna-cza to, że możemy taką strukturę danych w locie mo-dyfikować (np. dodawać lub usuwać z niej elementy), wiedząc że operacje wyszukiwania nadal będą dzia-łać z rozsądną szybkością.

Jednakże, czasami występują sytuacje, kiedy z góry znamy scenariusz przetwarzania danych i wiemy, że taka uniwersalność nie będzie nam potrzebna. Roz-ważmy takie zadanie: musimy zliczyć liczbę wystąpień z góry określonego, niezmiennego zbioru wyrazów w zadanym tekście. Na przykład możemy liczyć wy-stąpienia słów kluczowych języka C++. W tej sytuacji możemy na samym początku umieścić zbiór interesu-jących nas słów w kontenerze asocjacyjnym, zainicjo-wać odpowiadające im licznik wartością zero i rozpo-cząć przeszukiwanie tekstu. Jeśli w tym przypadku skorzystamy z szablonu klasy std::map, to będziemy płacić za jej uniwersalność spadkiem wydajności. Mi-mo wszystko spójrzmy jak takie zadanie można zreali-zować przy pomocy kontenera std::map (Listing 3).

W przedstawionym przykładzie na początku wczy-tuję wszystkie słowa kluczowe języka C++ z pliku keywords.txt (żeby zaoszczędzić sobie pisania sko-piowałem je ze strony http://cs.smu.ca/~porter/csc/ref/cpp_keywords.html) i umieszczam je w kontene-rze std::map. Następnie wczytuję po kolei słowa z pli-ku test.txt (w ramach testu umieściłem w tym pliku zawartość Listingu 3). Warto jednak zauważyć, że nie używam już konstrukcji histogram[word]++.

W zamian za to przy pomocy metody std::map::find() wyszukuję zadany wyraz. Z konstrukcją histogram[word]++ problem jest taki, iż odgrywa ona rolę lukru syntaktycznego, zaś ten jak, jak mawiał Alan Perlis, powoduje raka średnika... W naszym przypadku użycie tej konstrukcji powodowałoby nie-potrzebne wstawianie nowych elementów do konte-nera (taki jest efekt uboczny korzystania z niej).

Efekt działania programu jest następujący (zakła-dając, że umieścisz w plikach wejściowych to samo co ja):

if -> 2int -> 3namespace -> 1typedef -> 1using -> 1while -> 2.

Zauważ, że na Listingu 3 umieściłem spacje po słowie kluczowym int w definicjach typu map<string, int >.

Listing 3. Zliczanie wystąpień słów kluczowych języka C++ przy pomocy kontenera std::map

#include <cassert>

#include <fstream>

#include <iostream>

#include <map>

#include <string>

#include <boost/foreach.hpp>

#define foreach BOOST_FOREACH

using namespace std;

int main()

{

fstream fis("keywords.txt");

assert(!!fis);

string keyword;

map<string, int > histogram;

while (fis >> keyword)

{

histogram[keyword] = 0;

}

fstream fis2("test.txt");

assert(!!fis2);

string word;

while (fis2 >> word)

{

map<string, int >::iterator found =

histogram.find(word);

if (found != histogram.end())

{

found->second++;

}

}

typedef map<string, int >::value_type histogram_

entry;

foreach (const histogram_entry& entry, histogram)

{

if (entry.second > 0)

{

cout << entry.first << " -> " <<

entry.second << endl;

}

}

}

Page 10: SDJ 08 2010

8/201010

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 11

Zrobiłem to dlatego, że strumień rozdziela wyrazy tyl-ko na podstawie białych znaków.

W tym przypadku aż się prosi, aby skorzystać z nie-co bardziej efektywnego kontenera. Wprowadźmy więc drobne modyfikacje w naszym przykładowym programie z Listingu 3 (patrz: Listing 4).

Oprócz kliku zmian związanych z dołożeniem od-powiednich nagłówków i deklaracji użycia przestrze-ni nazw (o czym bardziej szczegółowo za moment), w samym kodzie zmieniło się bardzo niewiele. Pod-mieniłem w zasadzie tylko typ kontenera asocjacyj-nego. Dodałem przy okazji odpowiednią definicję ty-pu:

typedef sparse_hash_map<string, int, hash_

compare<string> >

histogram_type;

tak aby na przyszłość łatwiej było wprowadzać takie modyfikacje. Chwilka niepewności w trakcie kompi-lacji programu i... udało się! Magia STL'a zadziałała! Po uruchomieniu pogram uraczył mnie następujący-mi danymi na konsoli:

if -> 2using -> 1while -> 2typedef -> 1namespace -> 1int -> 3.

Jeśli spojrzymy na wartości liczników poszczególnych dla słów kluczowych to zauważymy, iż uzyskaliśmy do-kładnie taki sam wynik jak w przypadku działania pro-gramu z Listingu 3. Tam jednak poszczególne pary

Listing 4. Zliczanie wystąpień słów kluczowych języka C++ przy pomocy kontenera google::sparse_hash_map

#include <cassert>

#include <fstream>

#include <hash_map>

#include <iostream>

#include <map>

#include <string>

#include <boost/foreach.hpp>

#include <google/sparse_hash_map>

#define foreach BOOST_FOREACH

using namespace google;

using namespace std;

using namespace stdext; // for hash_compare

int main()

{

fstream fis("keywords.txt");

assert(!!fis);

string keyword;

typedef sparse_hash_map<string, int, hash_

compare<string> >

histogram_type;

histogram_type histogram;

while (fis >> keyword)

{

histogram[keyword] = 0;

}

fstream fis2("test.txt");

assert(!!fis2);

string word;

while (fis2 >> word)

{

histogram_type::iterator found =

histogram.find(word);

if (found != histogram.end())

{

found->second++;

}

}

typedef histogram_type::value_type histogram_

entry;

foreach (const histogram_entry& entry,

histogram)

{

if (entry.second > 0)

{

cout << entry.first << " -> " <<

entry.second << endl;

}

}

}

Page 11: SDJ 08 2010

8/201010

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 11

klucz/wartość były... posortowane! Wynika to z wła-ściwości struktury danych na której opera się kontener std::map (dzięki przechowywaniu elementów w takim porządku jest on w stanie zapewnić szybki dostęp do nich). Z kolei google::sparse _ hash _ map używa nieco odmiennej strategii (o żadnym sortowaniu nie ma mo-wy!). Jeśli interesujesz się jaka jest zasada działania kontenerów haszujących, zajrzyj do ramki Tablice ha-szujące: co tam jest pod maską?

Wiemy już, że bardzo łatwo jest podmienić sza-blon std::map na google::sparse_hash_map. Jednak-że czy w tym miejscu historia się kończy? Zdecydo-wanie nie! Rozważmy więc z jakimi różnicami mamy do czynienia.

Po pierwsze – nagłówki. Czy zwróciłeś drogi czytel-niku uwagę, że na Listingach 1 i 4 umieściłem nagłó-wek <hash_map>? Wygląda on jak nagłówek standar-dowy, jednakże pamięć Ciebie nie myli – takiego pli-ku nagłówkowego standard C++98 nie przewiduje... O co więc chodzi?

Sprawa jest prosta: nagłówek <hash_map> zawiera niestandardową implementację zgodnej z STL'em ta-blicy haszującej (stworzona przez firmę Dinkumware, która dostarcza implementację STL dla pakietu Micro-soft Visual C++). Kontener ten (jak i inne niestandar-dowe rozszerzenia dostarczone przez Dinkumware) umieszczone są w przestrzeni nazw stdext. Autor bi-blioteki Google SparseHash postanowił zaimplemen-tować swoją bibliotekę tak, aby była ona jak najbar-dziej kompatybilna z istniejącymi na rynku rozwiąza-niami. Dlatego też google::sparse_hash_map wykorzy-stuje tę samą infrastrukturę haszowania, co natywne rozwiązania dostarczone dla programistów w ramach ich pakietów narzędziowych. Konkretnie mowa tu-taj o szablonie hash_compare, który definiuje politykę tworzenia i porównywania skrótów. Co ciekawe, na platformach wywodzących się z rodziny Unix (tj. na których dostępny jest kompilator GCC) przedstawio-ne tutaj przykłady by się nie skompilowały. Dlacze-go? Otóż, GCC używa innej implementacji niestan-dardowego kontenera haszujacego, wywodzącej się z oryginalnej wersji biblioteki STL rodem z firmy SGI. Na Listingu 5 przedstawiłem zmodyfikowany przykład z Listingu 1, współpracujący z tą implementacją.

Widać tutaj dobitnie, że implementacja kompatybil-na z SGI ma zupełnie inne parametry szablonowe. W rzeczywistości, różnice pomiędzy tymi implemen-tacjami idą o wiele dalej (ale to w sumie dobry temat na oddzielny artykuł traktujących o szczegółach im-plementacji kontenerów haszujących).

Jak to!? – zapytają zapewne niektórzy Czytelni-cy. A co z przenośnością kodu? Cóż – na tym eta-pie standaryzacji (a w zasadzie jej braku) chyba po-zostaje się z tą niedogodnością pogodzić. Najpraw-dopdobniej nowy standard języka C++ coś poprawi

w tym zakresie, ale na niego musimy jeszcze trochę poczekać.

Jeśli chodzi o funkcjonalność google::sparse_hash_map, to spełnia on wymagania określone w ramach konceptu Pair Associative Container, stanowiącego część dokumentacji biblioteki STL. Te same wymaga-nia spełnia kontener std::map, więc na poziomie in-

Listing 5. Prosty przypadek użycia biblioteki SparseHash, zmody�kowany pod kątem kompatybilności z kompilatorem GCC

#include <iostream>

#include <google/sparse_hash_map>

using namespace google;

using namespace std;

using namespace ext;

struct eqstr

{

bool operator()(const char* s1, const char* s2) const

{

return (s1 == s2) || (s1 && s2 && strcmp(s1, s2)

== 0);

}

};

int main()

{

sparse_hash_map<const char*, int, hash<const char*>,

eqstr> months;

months["january"] = 31;

months["february"] = 28;

months["march"] = 31;

months["april"] = 30;

months["may"] = 31;

months["june"] = 30;

months["july"] = 31;

months["august"] = 31;

months["september"] = 30;

months["october"] = 31;

months["november"] = 30;

months["december"] = 31;

cout << "september -> " << months["september"] <<

endl;

cout << "april -> " << months["april"] <<

endl;

cout << "june -> " << months["june"] << endl;

cout << "november -> " << months["november"] <<

endl;

}

Page 12: SDJ 08 2010

8/201012

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 13

terfejsów komponenty te (przynajmniej w teorii) moż-na bez problemu wymieniać. Oczywiście, nadal po-zostają pewne drobne kwestie wynikające ze szcze-gółów implementacyjnych. Szczególną uwagę należy zwrócić na następujące kwestie:

• w przypadku gdy chcemy korzystać z meto-dy erase() należy zaraz po konstrukcji obiek-tu google::sparse _ hash _ map wywołać metodę set _ deleted _ key(),

• w sytuacji kiedy element kontenera jest usuwany, przydzielona mu pamięć nie jest z miejsca zwal-niana (to podejście pozwala iterować po kontene-rze i wywoływać metodę erase() nie powodując unieważnienia wskazujących na niego iteratorów),

• ustawiając minimalny poziom obciążenia (metoda min _ load _ factor()) na na wartość 0.0 można uzyskać gwarancję, że google::sparse _ hash _

map nigdy nie zmniejszy swojej objętości.

Wszystkie przedstawione wyżej rozważania tyczą się zarówno kontenera google::sparse _ hash _ map jak i google::dense _ hash _ map. Różnice pomiędzy tymi kontenerami wiążą się jedynie z ich charaktery-stykami wydajnościowymi. Na jakich więc zasadach dokonywać wyboru pomiędzy jednym a drugim? Oto garść porad:

• Jeśli zależy Ci na minimalizacji zużycia pamię-ci, wybierz google::sparse _ hash _ map, . Powinie-neś się jednak liczyć z faktem, iż wykonywane na niej operacji mogą być od trzech do siedmiu ra-zy wolniejsze w porównaniu do jej bliźniaczej im-plementacji; szczególną uwagę należy zwrócić na operacje wstawiania – tutaj spadek wydajno-ści jest największy. Za to narzut związany ze zu-życiem pamięci w przypadku google::sparse _

hash _ map wynosi tylko cztery bity per przecho-wywany element.

• Jeśli zależy Ci na ekstremalnej wydajności, kon-tener google::dense _ hash _ map jest zapewne tym czego szukasz. Używając go pamiętaj, że wprowadza on około 78-procentowy narzut zwią-zany z wykorzystaniem pamięci (jeśli elementy przechowywane w kontenerze zajmują n bajtów, to narzut wprowadzony przez google::dense _

hash _ map wynosi n*0.78). W świetle powyższych faktów można stwierdzić, że ten wariant kontene-ra jest idealny w przypadku gdy przechowujemy w nimi niewiele danych i wykonujemy na nim bar-dzo dużo operacji wyszukiwania (byłby dobrym kandydatem do zastąpienia google::sparse _

hash _ map w przykładzie z Listingu 4).• Pamiętaj, że zarówno google::sparse _ hash _ map

jak i google::sparse _ hash _ map to kontenery wy-

specjalizowane (można by powiedzieć: rozwiąza-nia ekstremalne dedykowane do specjalnych za-stosowań). Jako bardziej uniwersalną alternaty-wę możesz rozważyć niestandardowe kontenery haszujące dołączone jako rozszerzenia w więk-szości nowoczesnych kompilatorów. Implementa-cje tych kontenerów są zazwyczaj bardziej zrów-

Listing 6. Prosty przypadek użycia kontenera google::dense _ hash _ set

#include <hash_set>

#include <iostream>

#include <google/dense_hash_set>

using namespace google;

using namespace std;

using namespace stdext; // for hash_compare

typedef dense_hash_set<const char*, hash_compare<const

char*> >

DenseHashSetCPtr;

void lookup(const DenseHashSetCPtr& set, const char*

element)

{

cout << element

<< " "

<< ((set.find(element) != set.end())

? "found"

: "not found")

<< endl;

}

int main()

{

DenseHashSetCPtr fruits;

fruits.set_empty_key("");

fruits.insert("kiwi");

fruits.insert("plum");

fruits.insert("apple");

fruits.insert("mango");

fruits.insert("apricot");

fruits.insert("banana");

lookup(fruits, "mango");

lookup(fruits, "apple");

lookup(fruits, "durian");

return 0;

}

Page 13: SDJ 08 2010

8/201012

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 13

Listing 6. Przykład użycia kontenera z Google SparseHash z wybranymi komponentami bibliotek STL i Boost

#include <algorithm>

#include <hash_map>

#include <iostream>

#include <string>

#include <google/sparse_hash_map>

#include <boost/shared_ptr.hpp>

using namespace boost;

using namespace google;

using namespace std;

using namespace stdext; // for hash_compare

struct Employee

{

Employee(string firstName,

string lastName,

int age) :

m_firstName(firstName),

m_lastName(lastName),

m_age(age)

{

}

string m_firstName;

string m_lastName;

int m_age;

};

typedef int Id;

typedef shared_ptr<Employee> EmployeePtr;

typedef sparse_hash_map<Id, EmployeePtr, hash_compare<Id> >

EmployeeMap;

std::ostream& operator<<(std::ostream& os, EmployeeMap::value_type emplyeeEntry)

{

return os << '['

<< emplyeeEntry.second->m_firstName

<< ' '

<< emplyeeEntry.second->m_lastName

<< ", age=" << emplyeeEntry.second->m_age << ']';

}

int main()

{

EmployeeMap employees;

employees[100] = EmployeePtr(new Employee("Jan", "Kowalski", 30));

employees[101] = EmployeePtr(new Employee("Adam", "Nowak", 31));

employees[102] = EmployeePtr(new Employee("Stefan", "Kowalski", 29));

copy(employees.begin(), employees.end(),

std::ostream_iterator<EmployeeMap::value_type>(cout, "\n"));

return 0;

}

Page 14: SDJ 08 2010

8/201014

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 15

noważone zarówno pod kątem zużycia pamięci i efektywności.

SparseHash: zbioryZbiór w nomenklaturze biblioteki STL to kontener za-wierający zestaw unikalnych elementów i oferują-cy operacje pozwalające w szybki sposób przeko-nać się czy dany element występuje w określonym zbiorze, czy nie. Podobnie jak std::map, standardo-wy kontener zbiór z biblioteki STL (std::set) zaim-plementowany jest jako zrównoważone drzewo. Zale-ty i wady takiego rozwiązania są podobne jak w przy-padku std::map (opisanych w poprzednim punkcie ni-niejszego artykułu).

Biblioteka Google SparseHash oferuje dwa zamien-niki dla kontenera std::set: google::sparse_hash_set oraz google::dense_hash_set. Na Listingu 6 pokaza-ny jest prosty przykład użycia google::dense_hash_set (kompatybilny z kompilatorami z rodziny Micro-soft Visual C++).

To na co warto zwrócić uwagę, to konieczność wy-wołania metody set_empty_key() na zbiorze, zaraz po jego konstrukcji. Zaniechanie tej czynności spowodu-je wystąpienie asercji przy próbie wywołania meto-dy insert().

Generalnie cała dyskusja odnośnie wydajności, na-rzutów i wskazówek kiedy używać (bądź nie używać) prezentowanych kontenerów, przedstawiona w po-przednim punkcie tego artykułu (w kontekście ko-

lekcji asocjacyjnych), pozostaje w mocy w odniesie-niu do zbiorów dostępnych w ramach Google Sparse-Hash. W związku z tym nie będę jej powtarzał w tym miejscu aby uniknąć redundancji.

Dodatkowe smaczkiKontenery STL mają to do siebie, że posiadają jak to się potocznie mówi tzw. podwójne dno. Innymi sło-wy, zaprojektowano je w ten sposób aby były jak naj-prostsze w użyciu dla przeciętnego (mało wymagają-cego użytkownika). Z drugiej strony, użytkownicy bar-dziej zaawansowani (lub Ci, którzy z jakichś przyczyn muszą dopasować kontenery do swoich potrzeb) ma-ją możliwość ich dostrojenia. Kluczem jest w tym przypadku zrozumienie znaczenia wszystkich para-metrów, które przyjmują szablony klas implementu-jące kontenery (część z nich ma wartości domyślne, aby niepotrzebnie nie zaprzątać głowy przeciętnych użytkowników biblioteki).

Kontenery haszujące można dostrajać na dwa spo-soby: modyfikując funkcje odpowiedzialne za tworze-nie skrótów (tzw. funkcje haszujące) oraz podłącza-jąc swój własny alokator pamięci.

W pierwszym przypadku mowa jest o trzecim para-metrze występujących w szablonach klasy z bibliote-ki Google SparseHash. Parametr ten jest zależny od tego z jakim kompilatorem pracujemy (w przypadku MSVC wymagany jest obiekt funkcyjny zgodny z in-terfejsem hash_comparator zaś w przypadku kompila-

W Sieci• http://code.google.com/p/google-sparsehash/ – strona domowa biblioteki Google SparseHash.• http://google-sparsehash.googlecode.com/svn/trunk/doc/index.html – dokumentacja techniczna biblioteki Google Sparse-

Hash.• http://google-sparsehash.googlecode.com/svn/trunk/doc/implementation.html – opis implementacji kontenerów wchodzą-

cych w skład biblioteki Google SparseHash.• http://google-sparsehash.googlecode.com/svn/trunk/doc/performance.html – analiza wydajności kontenerów wchodzących

w skład biblioteki Google SparseHash.

Tablice haszujące: co tam jest pod maską?W informatyce tablica haszująca (zwana również tablicą mieszającą) to jedna z odmian tablicy asocjacyjnej, tj. struktury danych służącej do przechowywania informacji, w taki sposób aby możliwy był do nich szybki dostęp.Odwołania do przechowywanych elementów dokonywane są na podstawie klucza, który identyfikuje elementy umieszczone w tablicy. Kluczem może być na przykład PESEL pracownika, a wyszukiwaną informacją jego dane adresowe.Strategie implementacji kontenerów asocjacyjnych są różne. W przypadku stosowania tablicy haszującej stosuje się tzw. funk-cję haszujacą (zwaną też funkcją mieszającą), która dla danego klucza wyznacza indeks w tablicy. Innymi słowy przekształca ona klucz w liczbę z zadanego zakresu.Funkcje mieszające są zwykle nieskomplikowane, tak aby czas potrzebny na ich wykonywanie nie stanowił zbyt dużego na-rzutu. W najprostszym przypadku wartość funkcji mieszającej, obliczona dla danego klucza, wyznacza dokładnie indeks po-szukiwanego elementu. Jeżeli miejsce wskazywane przez obliczony indeks jest puste, to poszukiwanej informacji nie ma w ta-blicy. W ten sposób wyszukiwanie elementu ma złożoność czasową O(1). Oznacza to, że dostęp do elementów umieszczonych w takiej tablicy można (w optymistycznym przypadku) uzyskać w czasie stałym! W przypadku tablic mieszajacych pojawia się jednak dość poważny problem w postaci tzw. kolizji. Problem ten manifestuje się w sytuacji gdy funkcję mieszająca wygene-ruje te same wartości dla dwóch różny kluczy. Problem ten można obejść na kilka sposobów, zazwyczaj kosztem wydajności operacji wykonywanych na tablicy.

Page 15: SDJ 08 2010

8/201014

BIBLIOTEKA MIESIĄCA Tablice haszujące w języku C++

www.sdjournal.org 15

torów używających wersji SDL zgodnych z SGI, na-leży przekazać obiekt funkcyjny zgodny z interfej-sem hash oraz, dodatkowo, obiekt funkcyjny odpo-wiedzialny za porównywanie elementów). Różnice te wynikają między innym z tego, że w implementa-cji kontenerów haszujących autorstwa firmy Dinkum-ware obiekty porównywane są na zasadzie równo-ważności (ang. equivalence), czyli za pomocą opera-tora <, zaś w implementacji rodem z SGI porównuje się obiekty na zasadzie równości (ang. equality). Wy-jaśnienie różnicy pomiędzy tymi pojęciami wykracza poza tematykę niniejszego artykułu. Więcej informa-cji na ten temat możesz szukać w dokumentacji do-starczonej do Twojego kompilatora.

Od wersji 1.7 Google SparseHash pozwala podłą-czać do kontenerów alokatory pamięci definiowane przez użytkowników (we wcześniejszych wersjach bi-blioteki szablonowy parametr określający alokator był po prostu ignorowany). Domyślny alokator dołączony razem z biblioteką opera się na funkcjach malloc() i free() ze standardowej biblioteki języka C.

Jeszcze jednym kluczem do optymalnego wykorzy-stania biblioteki jest dokładne zrozumienie zasad jej działania. Na szczęście, firma Google jest w tym za-kresie bardzo liberalna i pozwala swoim pracowni-kom publikować informacje na temat szczegółów im-plementacji tworzonych przez nich rozwiązań. Link do strony WWW na której opisane są detale imple-mentacji Google SparseHash znajduje się w ramce W sieci.

Miła niespodziankaAutor biblioteki Google SparseHash przygotował do jej użytkowników miłą niespodziankę w postaci wbu-dowanych funkcji pozwalających serializować i dese-rializować oferowane przez nią kontenery na dysk. Serializacja/deserializacja odbywa się w dwóch eta-pach: najpierw zapisywane są metadane (metoda: write_metadata()) a następnie, dane właściwe (me-toda: write_nopointer_data()). Takie podejście dzia-ła co prawda tylko z danymi prostymi (typy podsta-wowe języka C, oraz struktury złożone z tych typów). W przypadku gdy w mamy do czynienia ze wskaźni-kami czy obiektami języka C++, pracy jest nieco wię-cej, ale zadanie nadal jest wykonalne (szczegółowe

informacje jak to zrobić można znaleźć w dokumen-tacji biblioteki).

W sumie – mała rzecz, a cieszy...

Współpraca między narodami......to niewątpliwie kwestia bardzo ważna. Nas jed-nak bardziej interesuje współpraca pomiędzy biblio-tekami. W tym zakresie Google SparseHash sprawu-je się znakomicie. Prawdę powiedziawszy – w tym le-ży spora część jej siły. Zarówno komponenty z STL jak i z Boost nie są jej straszne. W poprzednich przy-kładach widać było gołym okiem, że omawiane kon-tenery bezproblemowo współpracują z makrem BO-OST_FOREACH. Na Listingu 7 pokazałem jeszcze jeden przykład, który udowadnia iż Google Sparse-Hash potrafi współpracować z inteligentnymi wskaź-nikami z Boost oraz z algorytmami z STL.

PodsumowanieTak oto dobrnęliśmy do końca artykułu omawiającego bibliotekę Google SparseHash, oferującą zoptymali-zowane pod względem zużycia pamięci i czasu im-plementacje kontenerów haszujących, zgodnych ze standardami STL. Marka Google stojąca za bibliote-ką mówi sama za siebie. Biblioteka SparseHash, in-tensywnie wykorzystywana w infrastrukturze Giganta z Mountain View, to świetnie spełniający swoje zada-nie kawałek kodu i mogę z czystym sumieniem pole-cić go każdemu programiście języka C++, który szu-ka wydajnych kontenerów haszujących.

Mam nadzieję, że ten artykuł zachęci Ciebie dro-gi czytelniku do poczynienia eksperymentów z nowy-mi typami kolekcji. Czas po temu najwyższy – nowy standard języka C++ nadchodzi, a wraz z nimi – no-we możliwości. Jednakże nawet gdy nowe kontenery asocjacyjne (unordered_map i unordered_set) zagosz-czą już pod nasze strzechy, wyspecjalizowane kolek-cje z biblioteki Google SparseHash najprawdopodob-niej nadal będą znajdować niejedno zastosowanie.

LicencjaBiblioteka Google Protocol Buffers jest udostępniana na nowej licencji BSD (ang. New BSD License). Licencja ta prze-widuje możliwość wykorzystywania biblioteki bez uiszcza-nia żadnych opłat, tak w otwartych jak i w komercyjnych projektach. Szczegółowe informacje odnośnie tej licencji znajdziesz tutaj: http://www.opensource.org/licenses/bsd-li-cense.php.

RAFAŁ KOCISZ Pracuje na stanowisku Kierownika ds. Badań i Rozwoju w �r-mie Gamelion, wchodzącej w skład Grupy BLStream. Ra-fał specjalizuje się w technologiach związanych z produkcją oprogramowania na platformy mobilne, ze szczególnym na-ciskiem na tworzenie gier.Grupa BLStream powstała by efektywniej wykorzystywać potencjał dwóch, szybko rozwijających się producentów oprogramowania – BLStream i Gamelion. Firmy wchodzące w skład grupy specjalizują się w wytwarzaniu oprogramowa-nia dla klientów korporacyjnych, w rozwiązaniach mobilnych oraz produkcji i testowaniu gier.Kontakt z autorem: [email protected]

Page 16: SDJ 08 2010

8/201016

KLUB TECHNICZNY Technologie Progress OpenEdge

www.sdjournal.org 17

Nieraz w cyklu poświęconym technologiom Ope-nEdge podkreślane były bardzo ważne cechy tworzonych aplikacji biznesowych: ich otwar-

tość, elastyczność i dostępność. Zapoznamy się teraz ze standardowym, otwartym interfejsem SQL, który za-pewnia szybki dostęp do relacyjnych baz OpenEdge. Bazy te wraz z interfejsem dają możliwość osiągnięcia wydajnej integracji z aplikacjami/narzędziami do rapor-towania, rozwoju czy przetwarzania transakcyjnego.

OpenEdge SQL składa się następujących kompo-nentów:

• Sinik SQL, który instaluje się jako integralna część systemu baz danych OpenEdge. Silnik ten wspiera architekturę SOA poprzez niezawodną obsługę ty-pów danych, możliwość zmian schematu bazy on-line oraz optymalizację zapytań. Został on zapro-jektowany do osiągania maksymalnej skalowalno-ści i wydajności.

• Driver JDBC dla OpenEdge – autorstwa DataDi-rect Tetchnologies. Realizuje dostęp do systemu relacyjnych baz danych OpenEdge dla aplikacji opartych na technologii Java. Jest instalowany ja-ko część produktu OpenEdge SQL Client Access.

• Driver ODBC dla OpenEdge – również autorstwa DataDirect Tetchnologies. Realizuje dostęp do systemu relacyjnych baz danych OpenEdge dla aplikacji obsługujących interfejs ODBC. Jest insta-lowany jako część produktu OpenEdge SQL Client Access.

ArchitekturaInterfejs SQL został zaimplementowany w OpenEd-ge do pracy w architekturze klient-serwer, składają-cej się z silnika SQL oraz aplikacji klienckiej, która łą-czy się z bazą poprzez dwa dostępne interfejsy: JDBC API, ODBC API.

Rysunek 1 pokazuje przykładową architekturę klient-serwer, w której klienci ABL i OpenEdge SQL przyłą-

Technologie Progress OpenEdge. Część 9 OpenEdge SQL stworzony przez Progress Software Corporation jest implementacją powszechnie znanych standardów, włączając SQL-92, SQL-99 i SQL-2003. Jest częścią otwartego, elastycznego interfejsu i pełni niezwykle ważną rolę w procesie rozwoju nowoczesnych aplikacji biznesowych OpenEdge.

Dowiesz się:• O architekturze OpenEdge SQL;• O podstawach implementacji języka OpenEdge SQL.

Powinieneś wiedzieć:• Ogólne zagadnienia związane z systemami relacyjnych baz

danych.

Rysunek 1. Architektura klient-serwer ABL i SQL

ĀȀ̀ЀԀȀऀࠀ܀

ఀԀഀༀĀऀ

ကᄀഀԀ

ကᄀഀԀ

ကᄀഀԀ

ሀᄀጀఀ᐀ᔀ

ကᘀఀԀ̀ഀᄀ

ĀȀ̀ЀȀ̀ԀĀࠀ܀

ĀȀ̀ЀȀ̀Ԁऀࠀ

ĀȀ̀ЀԀȀༀĀऀ

ఀԀഀᜀကĀ᠀

ఀԀഀᤀကĀ᠀

Page 17: SDJ 08 2010

8/201016

KLUB TECHNICZNY Technologie Progress OpenEdge

www.sdjournal.org 17

Silnik OpenEdge SQL obsługuje specyfikację Java Transaction API (JTA) architektury J2EE, co umożliwia realizację rozproszonych transakcji SQL przez bazę OpenEdge. Baza pełni w tej architekturze rolę „mene-dżera zasobów”, który w celu koordynacji zatwierdza-nia i wycofywania rozproszonych transakcji opiera się na zewnętrznych menedżerach transakcji.

Aby korzystać z drivera JDBC, trzeba najpierw zde-finiować zmienne środowiskowe, a w szczególności CLASSPATH, która musi być ustawiona na każdej maszynie klienckiej. Wskazuje ona na lokalizację klas drivera Ope-nEdge JDBC. Definicja może wyglądać następująco:

CLASSPATH=$DLC/java/openedge.jar: $CLASSPATH

ODBCDriver ODBC (Open Database Connectivity) dla silni-ka OpenEdge SQL realizuje dostęp do systemu baz

czają się do serwera bazy danych. Procesy serwe-ra SQL wykonują operacje bazodanowe poprzez pa-mięć dzieloną, która jest wspólna dla wszystkich proce-sów przyłączonych do bazy. Klienci SQL łączą się z ba-zą danych poprzez procesy serwera SQL. Zdalni klienci ABL łączą się do bazy za pośrednictwem procesów ser-werów ABL. Dla bazy danych można wystartować jeden broker uruchamiający procesy serwerów obsługujących klientów ABL i SQL lub też dwa brokery dedykowane dla określonego typu klientów (ABL lub SQL).

JDBCJava Database Connectivity (JDBC) jest interfejsem aplikacji Java (API) pozwalającym instrukcjom SQL wykonywać operację na bazie danych. JDBC API skła-da się z klas napisanych w Javie. Driver JDBC dla baz OpenEdge konwertuje wywołania JDBC API na wywo-łania specyficzne dla bazy danych. OpenEdge korzy-sta z drivera JDBC Typ 4. Na Rysunku 2 przedstawio-no architekturę aplikacji Java z wykorzystaniem tego drivera.

Aplikacja Java zawiera wywołania JDBC API, które muszą być wykonane przy pomocy metod DriverManager.getConnection lub DataSource.getConnection. Meto-da getConnection realizuje połączenie z odpowiednim driverem JDBC. Klasa DriverManager lub DataSource odpowiada za zarządzanie tym połączeniem.

Rysunek 2. Schemat przyłączenie klienta Java do bazy poprzez driver JDBC

ĀȀ̀ЀԀȀ܀ĀऀࠀԀఀഀༀԀကᄀሀ

ఀ᠀ᔀ̀ᘀጀᜀ̀᐀ጀЀጀ܀

ĀȀ̀Ѐ̀Ԁऀࠀ܀̀ᤀᨀ᐀ऀࠀĀ܀

ఀഀༀఀကᄀ̀ༀሀऀЀऀ̀ጀༀఀऀ᐀ऀᔀĀᘀᜀ̀ༀ᠀ᤀ̀ࠀᜀ᐀

ఀऀЀ̀ ᔀ᐀Āऀ̀ༀᨀЀကЀ̀

ᬀᔀ̀Ԁఀᜀ

ᰀԀȀᴀԀȀ

ᰀ̀ᔀఀ̀ḀԀఀഀༀԀᰀἀ

Rysunek 3. Schemat przyłączenia klienta do bazy poprzez driver ODBC

ĀȀ̀Ѐ

ԀĀࠀ܀

ऀ̀ЀఀЀഀༀကЀᄀကሀጀ᐀

ĀȀ̀ЀԀఀऀࠀ܀

᠀Ȁ̀ȀᔀЀༀĀကᘀЀȀༀᜀĀ܀

ĀကᘀЀༀᜀĀ᠀

ഀༀကᄀࠀሀࠀጀᔀᘀ܀᐀

Page 18: SDJ 08 2010

8/201018

KLUB TECHNICZNY Technologie Progress OpenEdge

www.sdjournal.org 19

danych OpenEdge dla klienta ODBC. Driver tłumaczy wywołania ODBC na wywołania, które źródło danych potrafi przetworzyć i zwrócić dane do aplikacji.

W skład architektury ODBC (patrz Rysunek 3) wcho-dzą następujące komponenty:

• Aplikacja ODBC – dowolny program wywołujący funkcje ODBC i uruchamiający na ich podstawie instrukcje SQL.

• Menedżer drivera ODBC – warstwa zarządzają-ca komunikacją pomiędzy aplikacją a driverem ODBC. Przejmuje wywołania funkcji z aplikacji i przed ich przekazaniem do odpowiedniego dri-vera wykonuje kontrolę błędów. Umożliwia przyłą-czanie źródła danych podczas runtime'u.

• Driver ODBC – warstwa oprogramowania (naj-częściej DLL) przetwarzająca wywołania funkcji ODBC dla specyficznego źródła danych. Driver łą-czy się ze źródłem, tłumaczy standardowe instruk-

cje SQL na składnię zrozumiałą dla źró-dła danych i zwraca dane do aplikacji.• Źródło danych – system baz danych, system operacyjny na jakim pracuje oraz oprogramowanie sieciowe niezbędne do realizacji dostępu do źródła.

Konfiguracja ODBC na kliencie Windows nie powinna nastręczać wielu trudności. Należy wybrać w Panelu Sterowania Na-rzędzia Administracyjne, a następnie Źró-dła Danych (ODBC). Przykładowa konfi-guracja ODBC dla bazy danych OpenEd-ge została pokazana na Rysunku 4.

Język SQLNiniejszy artykuł nie jest nauką języka SQL, ale warto wspomnieć o jego trzech podzbiorach.

• SQL DCL (Data Control Language) od-powiada za część związaną z bezpie-czeństwem bazy danych. Obejmuje in-

strukcje GRANT, REVOKE, COMMIT i ROL-LBACK. Dwie pierwsze dotyczą uprawnień

użytkownika do oglądania i modyfikacji informacji w bazie danych.

Tworząc system zabezpieczeń, można posłużyć się podejściem zarówno OpenEdge SQL, jak i ABL. Mię-dzy tymi językami istnieje dość znacząca różnica. OpenEdge SQL jest systemem zamkniętym, gdzie zawsze wymagana jest identyfikacja użytkownika do przeprowadzania operacji na bazie. Administrator (SQL DBA) przydziela ponadto poziom uprawnień.

ABL jest systemem otwartym. Oznacza to, że użyt-kownik nie ma żadnych ograniczeń dostępu do da-nych w nowej bazie. Należy zdefiniować administrato-ra bazy ABL i uprawnienia użytkowników, a także za-kazać dostępu do bazy użytkownikom nie posiadają-cym id i hasła (tzw. Blank User). Można więc podsu-mować oba podejścia, że w SQL „wszystko, co nie jest dozwolone, jest zakazane”, a w ABL „wszystko, co nie jest zakazane, jest dozwolone”.

Rysunek 4. De�niowanie źródła danych ODBC

Rysunek 5. Przyłączenie się do narzędzia SQL Explorer

Page 19: SDJ 08 2010

8/201018

KLUB TECHNICZNY Technologie Progress OpenEdge

www.sdjournal.org 19

SQL DDL (Data Definition Language) dotyczy de-finiowania obiektów bazy danych i manipulacji nimi. Wyróżnia się tutaj komendy CREATE, ALTER i DROP do do-dawania, modyfikacji i usuwania obiektów bazy lub ca-łych baz danych..

SQL DML (Data Manipulation Language) związany jest z wykonywaniem operacji na samych danych i jest najistotniejszy z punktu widzenia logiki biznesowej. Najważniejsze instrukcje SQL DML to: SELECT, INSERT, UPDATE, DELETE.

Narzędzia SQLDeweloper ma w środowisku OpenEdge dwa gotowe narzędzia do wykonywania instrukcji języka OpenEd-ge SQL: SQL Explorer w środowisku znakowym oraz SQL Editor w graficznym środowisku OpenEdge Ar-chitect.

SQL Explorer jest narzędziem napisanym w języ-ku Java. Umożliwia połączenie się z bazą OpenEd-ge i wykonywanie na niej komendy języka SQL. Abu uruchomić to narzędzie, trzeba mieć skonfigurowane środowisko OpenEdge. Wystarczy w tym celu (w do-wolnym systemie operacyjnym) wywołać skrypt pro-env, ustawiający odpowiednie zmienne środowiskowe. Następnie należy uruchomić polecenie, którego pełna składnia jest następująca:

sqlexp -db database-name -S port | service-name -H host

-user userid -password

password

Niektóre parametry są opcjonalne. Przykładowa ko-menda może wyglądać dla lokalnej bazy jak na Ry-sunku 5.

W graficznym środowisku OpenEdge Architect moż-na posłużyć się narzędziem SQL Editor, będącym wi-dokiem perspektywy Database Navigator (Rysunek 6).

Zainteresowanych pełną implementacją języka Ope-nEdge SQL odsyłam do dokumentacji na stronach Progress Communities. W następnym odcinku opo-wiem o Web Serwisach.

Rysunek 6. SQL Editor

W sieci• http://communities.progress.com/pcom/community/psdn/openedge - Progress Software Developers Network, część ukierunko-

wana na zagadnienia 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.

PIOTR TUCHOLSKI Autor jest od 12 lat związany z technologiami Progress So-ftware. Wieloletni konsultant i Kierownik Działu Szkoleń Pro-gress Software sp. z o.o., specjalizujący się w technologii Ope-nEdge. Kontakt z autorem: [email protected]

Page 20: SDJ 08 2010

8/201020

PROGRAMOWANIE PYTHON Kurs Pythona. Cz II – Struktury danych, funkcje i moduły

www.sdjournal.org 21

Ta część kursu zawiera bardzo wiele nowego materiału dla osób, które nie miały wcześniej styczności z językiem. Przedstawiony tu zakres

jest omawiany w praktycznie każdej książce poświę-conej językowi. W tym kursie jednak za punkt hono-ru przyjęliśmy sobie promowanie dobrych zwyczajów nad suchą prezentację faktów.

Język to budulec, narzędzie służące do opisu zacho-wania systemu komputerowego. Od powstania pierw-szego języka programowania trwa nieustanna walka o to, żeby produkowany kod źródłowy prowadził do oczekiwanego zachowania komputera. Mówimy, "że-by był poprawny", "bez błędów".

Kompilowane języki programowania takie jak C czy Java w swojej kulturze kultywują sprawdzanie na pro-gramiście na każdym kroku, czy jest pewien swoich poczynań. Składnia wielu fundamentalnych wyrażeń jest celowo bardzo rozwlekła i nadmierna, żeby kon-trolowała programistę przed pomyłkami, chwilowymi zaćmieniami umysłu. W szczególności Java jest języ-kiem, w którego kulturze kultywuje się ścisłą kontrolę nad tym, co programista powinien móc zrobić, co po-winien widzieć w danym kontekście, do czego powi-nien mieć w danym momencie dostęp. O ile takie za-chowanie jest bardzo pomocne, w szczególności na początku, dla programistów o niewielkim doświadcze-niu, po jakimś czasie zaczyna ciążyć zaawansowanym programistom jak kula u nogi.

Python idzie natomiast zupełnie pod prąd, traktując swoich programistów od samego początku jak ludzi in-teligentnych.

Wszyscy tutaj jesteśmy dorośliPo kilku latach intensywnego kodowania w Javie za-cząłem mieć wrażenie, że zarówno sam język, jak i je-go kompilator i maszyna wirtualna traktują mnie jak małe dziecko. Na każdym kroku musiałem wprost i ja-sno przedstawiać swoje zamiary i założenia, język nie dawał mi tutaj żadnej dowolności. Nie pozosta-wiając pola do domysłów, minimalizował liczbę uste-rek. Lub tak mu się wydawało. Kompilator krytykował wiele sprytnych manewrów, oceniając je za podejrza-ne lub potencjalnie niebezpieczne. W końcu sama ma-szyna wirtualna ukrywała w czasie uruchamiania wie-le kwestii w obawie, że użyłbym ich w sposób nieod-powiedzialny.

Python wygląda w porównaniu jak wejście w doro-słość. Nadal zachowania w oczywisty sposób szkodli-we lub nieprzemyślane powodują krytykę interpretera, jednak zostawia on bardzo wiele dowolności w sposo-bie wykorzystania języka przez programistę. Python zakłada, że wiesz, co robisz.

Środowisko programistów Pythona od pierwszych dni istnienia miało świadomość swobody, jaką on da-je, i wiążącej się z nią odpowiedzialności. Przez la-ta intensywnego wykorzystania języka środowisko to

Kurs Pythona. Cz. II – Struktury danych, funkcje i modułyW odcinku wprowadzającym zainstalowaliśmy Pythona i trochę pobawiliśmy się różnymi jego cechami. Po nabraniu swobody w wykorzystaniu linii poleceń możemy zabrać się za bardziej metodyczny przegląd tego, co oferuje nam język spod znaku węża.

Dowiesz się:• Jak zainstalować i uruchomić interpreter Python 2.6• Jak poruszać się po interaktywnej sesji w interpreterze

Powinieneś wiedzieć:• Czym różni się kultura języka Python od Javy i innych statycz-

nie typowanych języków• Jak używać wbudowanych w Pythona list, słowników, krotek i

zbiorów

Page 21: SDJ 08 2010

8/201020

PROGRAMOWANIE PYTHON Kurs Pythona. Cz II – Struktury danych, funkcje i moduły

www.sdjournal.org 21

kiedy elementy będą innych typów, program zwróci oczekiwany wynik, np. dla listy [1, 2.0, ["lista", "jako", "element"]]:

Element: 1

Element: 2.0

Element: ['lista', 'jako', 'element']

Gdzie tkwi tajemnica? To proste! W Pythonie wszyst-kie typy danych są obiektami. Wartość łańcuchową (%s) dla dowolnego typu interpreter uzyskuje przez wywołanie specjalnej metody _ _ str _ _ () na danym obiekcie (więcej o specjalnych metodach w Ramce 1 obok). Autorzy Pythona zauważyli więc, że wystar-czy wyposażyć możliwie dużo typów danych w taką specjalną metodkę i dla większości kodu źródłowe-go obiekty tych typów będą wyglądały zupełnie jak prawdziwe łańcuchy znaków. I nie ma znaczenia, że tak naprawdę to nie są łańcuchy. Nie jest to dla nas ważny szczegół, ważne, że działa. I to poprawnie!

W środowisku zakwitła więc jedna z najważniej-szych zasad kultury programowania w Pythonie: "je-żeli coś wygląda jak kaczka, chodzi jak kaczka i kwa-cze jak kaczka, to musi to być kaczka!". Oznacza to mniej więcej, że Twój kod powinien przyjmować do-wolne typy danych, które spełniają jakiś kontrakt (np. posiadają określoną metodę lub atrybut). Nie ma sen-su wieczne sprawdzanie, czy dana zmienna jest odpo-wiedniego typu. Zakładamy, że jest i że spełnia nasz kontrakt. Taki styl kodowania nazywany jest duck ty-ping. Co on nam daje?

• Po pierwsze: możliwość późniejszego rozszerze-nia programu w taki sposób, że pod daną zmien-ną (lub jako argument do danej funkcji) zostanie podana wartość innego typu, który jednak wyglą-da zupełnie jak oczekiwany typ! Szalenie przydat-na możliwość np. w testowaniu jednostkowym ko-du. I to bez żadnych zmian w oryginalnym kodzie!

• Po drugie: ryzyko, że w jakiś sposób trafi do nas niekompatybilny typ. Czy jednak jest się w tym przypadku o co martwić? Możemy niby sprawdzić, czy dana zmienna zawiera wartość danego typu, czy też może dany typ posiada oczekiwaną me-todę lub atrybut. Co jednak możemy zrobić, jeże-li okaże się, że nie? Domyślnym zachowaniem Py-thona jest rzucenie wyjątku. I nie wymaga to żad-nej pracy z naszej strony. Jeżeli chcielibyśmy wo-bec tego rzucić wyjątek w takim przypadku, nie ma sensu kodować rozwiązania, które naśladuje to, co i tak dzieje się domyślnie.

Poproszę indeksBiorąc pod uwagę duck typing, skupianie się na poje-dynczych strukturach danych nie jest tak bardzo cie-

wykształciło pewną kulturę, która opisuje rekomendo-wane rozwiązania różnych typowych kwestii. Ta kultu-ra daje wszystkim bazę do rozwijania oprogramowa-nia, które w sposób domyślny zachowuje się w spo-sób, którego inni programiści w środowisku oczekują, a którego kod źródłowy jest łatwy do czytania i zrozu-mienia przez innych programistów.

Jak to w dorosłym życiu, zachowania zgodnego z normami kulturalnymi oczekuje się również po po-czątkujących, czyli także po Tobie. Duży nacisk w tym kursie, a w szczególności w tej części, poświęcimy na zapoznanie Cię z zasadami pythonowego savoir-vi-vre. Jak zobaczysz, zasady te nie powstały bez przy-czyny, a w istocie stoją za nimi mocne argumenty. Za-cznijmy od najbardziej znanej konwencji w kulturze Pythona...

Jeżeli kwacze jak kaczka...Jak wspomnieliśmy w pierwszej części naszego kur-su, Python jest językiem silnie typowanym, ale o dy-namicznym systemie typów. Oznacza to w praktyce, że zmienne (w tym argumenty funkcji) mogą przyjmo-wać argumenty dowolnych typów, ale typ jest rzeczą twardą, tzn. łańcuch znaków nigdy bez jawnej konwer-sji nie zachowa się jak liczba. Najłatwiej można to zro-zumieć, wpisując w linii poleceń:

>>> 10 == "10"

False

>>> 10 == int("10")

True

>>> str(10) == "10"

True

Jak widać, Python rozróżnia liczbę 10 od łańcucha znaków reprezentującego "10". Za pomocą jawnej konwersji możemy jednak w razie potrzeby przecho-dzić między typami. Dynamiczne, ale silne typowa-nie to podstawowa cecha języka. Umożliwia z jednej strony uzyskanie bardzo zwięzłego kodu, który nie tylko szybciej się pisze, ale też szybciej czyta:

first_name = "Winona"

birth_year = 1971

Środowisko programistów Pythona wykorzystuje fakt, że każda zmienna może potencjalnie przechowywać wartość dowolnego typu. Przykładowo, mamy kod, który wypisuje na ekranie elementy listy:

for elem in a_list:

print "Element: %s" % elem

Kod oczywiście działa dla elementów będących łań-cuchami znaków. Jak się jednak okazuje, również

Page 22: SDJ 08 2010

8/201022

PROGRAMOWANIE PYTHON Kurs Pythona. Cz II – Struktury danych, funkcje i moduły

www.sdjournal.org 23

kawe jak dokładne omówienie sposobu ich wykorzy-stania. W końcu zgodnie z naturą języka, wiele metod bezpośrednio przenosi się również na inne typy da-nych i działa w zupełnie przewidywalny sposób. Za-cznijmy od dostępu do danych przez indeks.

Struktury danych typu listy, krotki (ang. tuple, patrz Ramka 2), słowniki, łańcuchy znaków i inne umożli-wiają dostęp do danych, które przechowują przez bar-dzo sprytny mechanizm indeksowania. Spójrzmy na przykład:

>>> movies = ['Lucas', 'Beetlejuice', 'Dracula']

>>> movies[0]

'Lucas'

>>> movies[1]

'Beetlejuice'

>>> movies[2]

'Dracula'

>>> movies[3]

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

IndexError: list index out of range

>>> movies[-1]

'Dracula'

>>> movies[-2]

'Beetlejuice'

>>> movies[-3]

'Lucas'

>>> movies[-4]

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

IndexError: list index out of range

Jak widać, lista daje dostęp do danych po indeksie (licząc - jak w każdym rozsądnym języku - od zera [1]). Jeżeli wybierzemy niepoprawny indeks, lista rzu-ca wyjątek IndexError. Więcej o wyjątkach w dalszej części artykułu.

Podawany indeks może być ujemny, wówczas liczy-my "od końca". To nie jedyny sprytny manewr dostęp-ny przy okazji indeksowania. Python umożliwia rów-nież wycinanie (ang. slicing) fragmentów z większych struktur:

>>> sisters = ['Meg', 'Jo', 'Beth', 'Amy']

>>> sisters[0:3]

['Meg', 'Jo', 'Beth']

>>> sisters[0:-1]

['Meg', 'Jo', 'Beth']

>>> sisters[:3]

['Meg', 'Jo', 'Beth']

>>> sisters[:-1]

['Meg', 'Jo', 'Beth']

>>> sisters[1:4]

['Jo', 'Beth', 'Amy']

>>> sisters[1:]

['Jo', 'Beth', 'Amy']

>>> sisters[0:4:2]

['Meg', 'Beth']

>>> sisters[1:4:2]

['Jo', 'Amy']

>>> sisters[0::2]

['Meg', 'Beth']

>>> sisters[::2]

['Meg', 'Beth']

Ramka 1: Metody specjalneOd pierwszego dnia w Pythonie istnieje grupa wbudowanych funkcji, które spełniają określoną funkcjonalność dla podanych ar-gumentów. I to niezależnie od typu tych argumentów. Zwane z angielska builtin functions lub po prostu builtins udo-stępniają m.in. możliwość sprawdzenia długości danej sekwencji (w tym: łańcucha znaków), porównania dwóch obiektów czy skonwertowania dowolnego typu na łańcuch znaków. Pełna lista builtins wraz z dokumentacją znajduje się pod adresem http://docs.python.org/library/functions.html.Kiedy tylko jest to możliwe, wykorzystanie builtins jest preferowane nad inne metody programowania. Stąd bardzo często spotkać można kod np.:

if len(s) < 10: ...

Obiekty w Pythonie implementują zachowanie poszczególnych builtinów w metodach specjalnych, które łatwo można poznać po dwóch podkreślnikach przed ich nazwą i po ich nazwie. Przykładowo:

>>> dir("")['__add__', ... , '__len__', ...]

Kiedy użytkownik wykona len(s) to interpreter w rzeczywistości wykonuje odpowiednią metodę specjalną s.__len__(). Jest to o tyle fajne, że definiując własne typy danych również możemy definiować metody specjalne, przez co umożliwiamy wyko-rzystanie builtinów na naszych typach. Jest to bardzo często wykorzystywana cecha języka, ponieważ ujednolicenie interfejsu nowych typów danych do już istniejących bardzo ułatwia zapamiętanie, jak z nich korzystać. Tak wyprodukowane API jest spój-ne, przewidywalne i intuicyjne. W Pythonie sprawdzanie długości, rozmiaru lub innych tego typu cech zawsze odbywa się przez len(). Jak jest w Javie? Mamy metody length(), atrybuty length, metody size(), count(), getSize(), itd. itp. Możliwe do zapamiętania? Z pewnością. Ale czy wygodne i intuicyjne?

Page 23: SDJ 08 2010

8/201022

PROGRAMOWANIE PYTHON Kurs Pythona. Cz II – Struktury danych, funkcje i moduły

www.sdjournal.org 23

Jak widać, jako "indeks" można podać zakres, skład-nia zakresów jest zresztą bardzo intuicyjna, a jedno-cześnie elastyczna. Można podawać zakresy, używa-jąc indeksu rozpoczynającego (włącznie), zamykają-cego (wyłącznie) lub obu. Indeks zamykający może być podawany jako ujemna wartość liczona elemen-tami od końca. Można też podać opcjonalnie krok, z jakim ma być dokonane wycinanie.

Efektem wycinania jest zupełnie nowa lista przecho-wująca elementy z oryginalnej listy. Oznacza to, że możemy bezstratnie tworzyć wiele skrawków z poje-dynczej listy i nie marnujemy w ten sposób pamięci, bo nowe listy przechowują referencje do tych samych elementów co oryginalna. Jest to często wykorzysty-wana właściwość, np. podczas ładnego wypisywania elementów:

>>> def print_mermaids():

... for mermaid in mermaids[:-1]:

... print "%s," % mermaid,

... print "%s." % mermaids[-1]

>>> print_mermaids()

Cher, Ryder, Ricci.

Zdefiniowaliśmy na szybko funkcję wypisującą nam aktorki grające w "Syrenach". Właściwy kod to tylko 3 linijki, a w elegancki sposób udało nam się uzyskać oddzielanie poszczególnych syrenek przecinkami, z kropką na końcu. To bardzo przydatny idiom, prędzej czy później w jakiś sposób będziesz go potrzebować.

Ale trochę się zapędziliśmy, o pętlach i funkcjach do-piero za chwilkę! Jeżeli dziwi Cię zachowanie wyraże-nia print, zapraszam do Ramki 3.

Wracając do indeksowania, zgodnie z naturą Py-thona przewidujemy, że w dokładnie ten sam spo-sób można korzystać z danych w łańcuchach znaków i krotkach. Faktycznie, nie ma żadnej różnicy:

>>> celebrity = "Winona Ryder"

>>> celebrity[0]

'W'

>>> celebrity[7]

'R'

>>> celebrity[-1]

'r'

>>> celebrity[:6]

'Winona'

>>> celebrity[7:]

'Ryder'

Nie jest to jednak najbardziej użyteczny sposób ma-nipulacji łańcuchami znaków, o manipulacji tekstem w ogólności, wyrażeniach regularnych i ich mocy wię-cej w przyszłym numerze Software Developer's Jour-nal.

Tymczasem spójrzmy jeszcze pokrótce na "indek-sowanie" w słownikach. W tym wypadku sprawa wy-gląda zgoła inaczej, ponieważ słowniki w swej natu-rze przechowują zbiory unikalnych kluczy, pod który-mi kryją się jakieś wartości. Dostęp do tych wartości przez klucz przypomina indeksowanie w listach, ale z oczywistych względów nie umożliwia zabawy w wy-cinanki. Spójrzmy na krótki przykład:

>>> movie_releases = {'Edward Scissorhands': 1990,

'Dracula': 1992, 'Star Trek': 2009}

>>> movie_releases['Star Trek']

2009

>>> movie_releases['Dracula']

1992

>>> movie_releases['A Scanner Darkly']

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

KeyError: 'A Scanner Darkly'

Jak widać, można się do przechowywanych obiek-tów odwoływać poprzez klucze, w przypadku użycia nieistniejącego klucza rzucany jest wyjątek KeyError. Nie ma możliwości odwołania się do "pierwszego" klucza albo "ostatniego" klucza, ponieważ słowni-ki przechowują z zasady nieuporządkowane zbio-ry danych. Fakt ten jest wykorzystywany przez Py-thona do optymalizacji struktury słownika w pamięci tak, żeby dostęp do dowolnego klucza był tak samo szybki. Może się więc zdarzyć, że wartości są prze-chowywane w innej kolejności niż je definiowaliśmy. Spójrzmy przykładowo na nasze movie _ releases z przykładu wyżej:

>>> movie_releases

{'Dracula': 1992, 'Edward Scissorhands': 1990, 'Star

Trek': 2009}

Jak widać, na moim komputerze interpreter zamienił miejscami Nożycorękiego z Drakulą. W Twoim przy-padku wynik może być inny, spróbuj.

Zgadnij kotku, co mam w środkuCo prawda interfejs dostępu do kluczy jest w przypad-ku słowników minimalistyczny, struktura danych wyna-gradza nam to jednak bogatym API do operowania na kluczach i wartościach. Zacznijmy od metody spraw-dzenia, czy dany klucz istnieje w słowniku:

>>> alien4 = {'Sigourney Weaver': 'Ellen Ripley',

... 'Winona Ryder': 'Annalee Call'}

>>> 'Winona Ryder' in alien4

True

>>> 'Johnny Depp' in alien4

False

Page 24: SDJ 08 2010

8/201024

PROGRAMOWANIE PYTHON Kurs Pythona. Cz II – Struktury danych, funkcje i moduły

www.sdjournal.org 25

Składnia ELEM in DICT jest bardzo przejrzysta i moż-na ją wykorzystać wszędzie, gdzie oczekujemy war-tości boolean, m.in. w instrukcjach warunkowych, wa-runkach wyjścia z pętli, itd. itp. Są też inne metody na sprawdzenie obecności klucza w słowniku, ale istnie-ją one już tylko z powodów historycznych i ich użycie nie jest zalecane.

Do słowników można oczywiście dokładać kolejne wartości, robimy to zasadniczo na dwa sposoby:

• dopisując nowy klucz bezpośrednio:

>>> alien4['Dan Hedaya'] = 'General Perez'

>>> alien4

{'Winona Ryder': 'Annalee Call',

'Sigourney Weaver': 'Ellen Ripley',

'Dan Hedaya': 'General Perez'}

• rozszerzając słownik za pomocą innego słownika:

>>> secondary_characters = {'Gary Dourdan': 'Christie',

... 'Ron Perlman': 'Johner',

... 'Dominique Pinon': 'Vriess'}

>>> alien4.update(secondary_characters)

>>> alien4

{'Dominique Pinon': 'Vriess', 'Winona Ryder': 'Annalee

Call',

'Sigourney Weaver': 'Ellen Ripley', 'Dan Hedaya':

'General Perez',

'Ron Perlman': 'Johner', 'Gary Dourdan': 'Christie'}

Obie metody są dobre, ta druga jest zalecana w przy-padku zmian w wielu kluczach jednocześnie. A co w przypadku, kiedy jakiś klucz chcemy usunąć ze słownika? Wykorzystujemy wyrażenie del:

>>> 'Dominique Pinon' in alien4

True

>>> del alien4['Dominique Pinon']

>>> 'Dominique Pinon' in alien4

False

>>> del alien4['Dominique Pinon']

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

KeyError: 'Dominique Pinon'

Jak widać, w przypadku gdy klucza nie ma w słowni-ku, podczas próby usunięcia elementu rzucany jest wyjątek KeyError.

Patrząc na metodę sprawdzania, czy dany element należy do słownika, albo na możliwość usuwania poje-dynczego klucza, wydaje się, że użyteczne byłoby coś podobnego dla list, krotek i zbiorów. Jak się okazuje, w Pythonie tak właśnie jest:

>>> numbers = [4, 8, 15, 16, 23, 42]

>>> numbers[0]

4

>>> numbers[3]

16

>>> 16 in numbers

True

>>> del numbers[3]

>>> 16 in numbers

False

>>> numbers

[4, 8, 15, 23, 42]

Na marginesie, sprawdzenie pierwszego indeksu, pod którym kryje się dana wartość w liście, można wyko-nać metodką index(), np.:

>>> numbers.index(42)

4

>>> numbers[4]

42

>>> numbers.index(44)

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

ValueError: list.index(x): x not in list

Oczywiście, jak opisuje Ramka 2, krotki są niezmie-nialne, więc nie możemy usuwać z nich elementów, natomiast do zbiorów nie da się odwołać po indeksie. Stąd też w zbiorach dodawanie i usuwanie elemen-tów trzeba rozwiązać inaczej:

>>> flatware = set(('fork', 'knife', 'spoon'))

>>> flatware.remove('fork')

>>> flatware.remove('spoon')

>>> flatware.add('spork')

>>> flatware

set(['spork', 'knife'])

>>> 'chopsticks' in flatware

False

>>> flatware.remove('chopsticks')

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

KeyError: 'chopsticks'

Funkcje i modułyW naszych dotychczasowych przykładach przemycili-śmy już kilka funkcji tam, gdzie inaczej nie dało się za-prezentować konkretnej konstrukcji językowej. Przyj-rzyjmy się jednak bliżej tym bestiom, bo w Pythonie są one wybitnie potężne, choć na pierwszy rzut oka tego nie widać.

Definiowanie funkcji jest proste jak budowa cepa, przykładowo:

Page 25: SDJ 08 2010

8/201024

PROGRAMOWANIE PYTHON Kurs Pythona. Cz II – Struktury danych, funkcje i moduły

www.sdjournal.org 25

Ramka 2: Listy, krotki i zbioryW Pythonie istnieje bardzo niewiele wbudowanych struktur danych, spełniają one jednak niezależne funkcje. Do przechowy-wania uszeregowanych danych dowolnych typów możemy wykorzystać listy oraz krotki (ang. tuples). Różnica między nimi jest taka, że listy są obiektami modyfikowalnymi (tzn. można do istniejącej listy dokładać nowe wartości, zmieniać je na inne oraz usuwać). Krotki są niezmienne, w celu uzyskania innej postaci krotki należy stworzyć nową:

>>> numbers = [1, 2, 3, 4]>>> numbers.append(5)>>> numbers[1, 2, 3, 4, 5]>>> numbers_tuple = (6, 7, 8)>>> numbers_tuple.append(9)Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'tuple' object has no attribute 'append'>>> del numbers[0]>>> numbers[2, 3, 4, 5]>>> del numbers_tuple[0]Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: 'tuple' object doesn't support item deletion>>> numbers.extend(numbers_tuple)>>> numbers[2, 3, 4, 5, 6, 7, 8]

Listy są ze swojej natury wolniejsze oraz zajmują więcej pamięci, stąd dla typowych sytuacji, gdzie wymagane jest zebranie wielu wartości bez potrzeby ich późniejszej modyfikacji, zaleca się wykorzystanie krotek. Można zresztą konwertować między tymi typami, np. kiedy chcemy udostępnić jako wynik funkcji niezmienialną postać jakiejś wcześniej budowanej listy (lub od-wrotnie):

>>> letters = ('a', 'b', 'c')>>> letters('a', 'b', 'c')>>> letters = list(letters)>>> letters.append('d')>>> letters['a', 'b', 'c', 'd']Zarówno listy, jak i krotki udostępniają możliwość sprawdzania, czy dany obiekt do nich należy:>>> 'b' in lettersTrue>>> 'b' in numbers_tupleFalse

Ze względu na budowę tych struktur takie wyszukanie ma liniową złożoność obliczeniową, więc dla licznych struktur lub częstych wykonań takich operacji jest to niewydajna operacja. Z tego względu Python udostępnia też dedykowany typ set jako nieuporządkowany zbiór wartości. W dodatku na tego typu obiektach można wykonywać też typowe logiczne operacje:

vowels = set(['a', 'e', 'i'])>>> hard_consonants = set(['k', 't', 'p'])>>> soft_consonants = set(['b', 'g', 'w'])>>> consonants = hard_consonants | soft_consonants>>> consonantsset(['p', 'b', 'g', 't', 'w', 'k'])>>> 'p' in consonantsTrue>>> 'k' in vowelsFalse>>> consonants & hard_consonantsset(['p', 'k', 't'])>>> consonants - hard_consonantsset(['b', 'w', 'g'])>>> vowels | consonantsset(['a', 'b', 'e', 'g', 'i', 'k', 'p', 't', 'w'])

Page 26: SDJ 08 2010

8/201026

PROGRAMOWANIE PYTHON Kurs Pythona. Cz II – Struktury danych, funkcje i moduły

www.sdjournal.org 27

def fib(n):

"""Returns the n-th fibonacci number.

>>> fib(1), fib(2), fib(3), fib(4)

(1, 1, 2, 3)

>>> fib(30)

832040

"""

if n in (0, 1):

return n

return fib(n-1) + fib(n-2)

Przykład jest bardzo prosty, jednak już tu widać kil-ka istotnych budulców prawdziwych funkcji: nagłó-wek z nazwą i przyjmowanymi argumentami, łańcuch dokumentujący (ang. docstring) wraz z przykładem użycia, który można uruchomić jako test jednostko-wy funkcji (ang. doctest). Sam kod jest bardzo prosty i przedstawia nieszczególnie wydajny sposób rekur-sywnego obliczania liczb ciągu Fibonacciego.

Jeżeli chcesz pobawić się funkcjami w trakcie czyta-nia, zapisz powyższy kod w pliku , dodając na końcu:

if __name__ == '__main__': #1

import sys if len(sys.argv) != 2:

raise SystemExit, "Wrong number of arguments"

num = int(sys.argv[1])

print fib(num)

Powyższy kod rozwiązuje nam w najprostszy po-prawny sposób kwestię obsługi linii poleceń. Żeby zrozumieć, co powyższe kilka linijek robi, cofnijmy się na moment. Każdy plik w Pythonie jest nazywa-ny modułem i może być importowany przez inne mo-duły w celu wykorzystania ich funkcjonalności. Impor-tów można dokonywać również w linii poleceń inter-pretera. Podczas pierwszego importu w ramach da-nego wykonania programu, kod modułu jest w cało-ści parsowany i wykonywany. Zupełnie tak samo par-sowany i wykonywany, jak gdybyśmy wykorzystywali nasz skrypt z linii poleceń:

$ python f.py

Wrong number of arguments

$ python f.py 20

6765

Linijka oznaczona komentarzem #1 istnieje wobec tego po to, żeby odróżnić wykonanie modułu jako samodzielnego programu z linii poleceń od impor-tu modułu przez inny moduł. W tym pierwszym przy-padku Python przypisuje modułowi specjalną na-zwę _ _ main _ _ . Jeżeli moduł jest importowany, pod zmienną _ _ name _ _ znajdzie się po prostu na-zwa modułu (w naszym przypadku f). Sprawdźmy

to, uruchamiając interpreter w katalogu, gdzie znaj-duje się plik f.py:

$ ls

f.py

$ python

Python 2.6.5 (r265:79063, Mar 26 2010, 16:07:38)

>>> import f

>>> f.__name__

'f'

>>> help(f.fib)

Help on function fib in module f:

fib(n)

Returns the n-th fibonacci number.

>>> fib(1), fib(2), fib(3), fib(4)

(1, 1, 2, 3)

>>> fib(30)

832040

>>> f.fib(20)

6765

Jak widać, można nasz moduł zaimportować i wyko-rzystać funkcję w innym programie. Modularyzacja jest podstawowym sposobem podziału kodu w Pythonie na logiczne komponenty. W przykładzie udało nam się też sprawdzić nazwę modułu oraz dokumentację funk-cji fib(). Jak widać, przykłady użycia w dokumentacji są w takim przypadku szalenie przydatne, bo można je bezpośrednio wykorzystać w interaktywnej sesji z in-terpreterem. Jak wspomniałem wyżej, przykłady te na-zywa się doctestami, ponieważ możemy je uruchomić, żeby sprawdzić, że funkcja działa poprawnie. Robimy to z konsoli, np. będąc w katalogu z plikiem f.py:

$ python -m doctest -v f.py

Trying:

fib(1), fib(2), fib(3), fib(4)

Expecting:

(1, 1, 2, 3)

ok

Trying:

fib(30)

Expecting:

832040

ok

1 items had no tests:

f

1 items passed all tests:

2 tests in f.fib

2 tests in 2 items.

2 passed and 0 failed.

Test passed.

Page 27: SDJ 08 2010

8/201026

PROGRAMOWANIE PYTHON Kurs Pythona. Cz II – Struktury danych, funkcje i moduły

www.sdjournal.org 27

O testowaniu jednostkowym opowiemy sobie dokład-niej w przyszłych odcinkach kursu [2], ale przyznaj, że motywująca jest świadomość, że nawet dokumen-tacja techniczna w kodzie może być w Pythonie for-mą testów jednostkowych. Przy tak wygodnych na-rzędziach nie wypada nie testować swojego kodu.

Zdefiniujmy sobie jeszcze jedną funkcję, żeby poroz-mawiać o innych ciekawych właściwościach tych kon-strukcji w Pythonie:

def extreme(seq, function=max):

"""Returns the extreme value of a sequence.

The value is computed using the provided function

(max by default).

>>> extreme((1,2,3))

3

>>> extreme([])

Traceback (most recent call last):

...

ValueError: max() arg is an empty sequence

>>> extreme((1,2,3), min)

1

>>> extreme(seq=(1,3,5,7), function=sum)

16

"""

return function(seq)

Podobnie jak w przypadku poprzedniej funkcji, doku-mentacja zajmuje więcej miejsca niż właściwy kod. Prezentujemy tu jednak za jednym zamachem kilka istotnych kwestii:

• w Pythonie funkcje mogą przyjmować argumen-ty opcjonalne. Specyfikuje się je, podając do argu-mentu w nagłówku funkcji jego wartość domyślną.

• dla Pythona funkcje są po prostu odrębnym ty-pem obiektów i jako takie mogą być swobodnie przekazywane jako zmienne lub argumenty do in-nych funkcji (jak w powyższym przykładzie). Funk-cje takie wykonujemy naturalnie tak, jakbyśmy wy-korzystywali funkcje zdefiniowane przez siebie lub zaimportowane z innych modułów

• w Pythonie podczas wykonania funkcji można po-dawać wartości argumentów wraz z nazwami ar-gumentów, do których mają być przypisane. Jest to szczególnie przydatne, jeżeli opcjonalnych ar-gumentów jest wiele i chcemy określić jasno, któ-ry z nich chcemy nadpisać. Dodatkowo, takie wy-konania jeszcze łatwiej się czyta, ponieważ wia-domo, jaką rolę spełnia dana wartość w wykona-niu funkcji.

Zabaw z funkcjami można wykonać jeszcze wiele, w przyszłym miesiącu zajmiemy się ich bardziej ezo-

terycznymi cechami przy okazji definiowania wła-snych klas.

Wyjątki od regułyJuż parokrotnie zarówno w tym odcinku, jak i w po-przednim, natrafialiśmy na różnego rodzaju wyjątki, które są rzucane w anormalnych sytuacjach, np. kiedy dany indeks w liście nie istnieje lub próbowano wyko-rzystać zły typ danych w jakimś miejscu.

W świecie Pythona każda sytuacja, którą programi-sta określiłby jako nietypową i wymagającą dodatko-wej uwagi w kodzie, jest obwarowana wyjątkami. Naj-częstsze rodzaje rzucanych wyjątków:

• nieudane wyszukanie (błędny indeks dla list i kro-tek, nieistniejący klucz w słowniku, brak oczekiwa-nego atrybutu w obiekcie itd.)

• wejścia-wyjścia (problem z odczytem pliku, zerwa-nie połączenia sieciowego, brak miejsca na dysku, brak pamięci itd.)

• arytmetyczne (próba dzielenia przez zero, prze-kroczenie zakresu liczbowego, błędna operacja zmiennoprzecinkowa itd.)

• nieoczekiwane wartości (zły typ danych, niepo-prawny argument)

• błędy w kodzie (błąd składniowy, niezadeklarowa-ne zmienne, asercja zawiodła, niepowodzenie im-portu zewnętrznego modułu, błąd z kodowaniem Unicode)

• zakończenia przetwarzania (koniec pliku, przerwa-nie iteracji, koniec generatora itd.)

Ramka 3: Wyrażenie printCzęsto używanym w naszych przykładach wyrażeniem jest print, o tyle specyficzna bestia, że w Pythonie 2.x nie jest to funkcja, tylko wyrażenie o dedykowanej składni. W naj-prostszym przypadku wykorzystujemy je tak:

>>> print "Hello world!"Hello world!>>> print "Hello", "world!"Hello world!>>> print 2, "years"2 years

Jak widać, wyrażenie przyjmuje dowolną liczbę argumen-tów (niekoniecznie łańcuchy znaków), które są na wyjściu oddzielane pojedynczą spacją. Możemy również pozosta-wić przecinek na końcu, w którym to przypadku print nie wstawia znaku końca linii na końcu tekstu, a jedynie spa-cję. W efekcie poniższy kod wyprodukuje tylko jedną linię tekstu:

>>> def goodbye():... print "Goodbye",... print "cruel",... print "world!">>> goodbye()Goodbye cruel world!

Page 28: SDJ 08 2010

8/201028

PROGRAMOWANIE PYTHON

Szczególnie ten ostatni rodzaj wyjątków budzi kon-trowersje użytkowników Pythona, czy takie sytuacje powinny być obsługiwane przez wyjątki. Tego rodza-ju wykorzystanie jest więc mocno ograniczone i ge-neralnie nie zachęca się do tworzenia własnych wy-jątków działających w analogicznych sytuacjach.

Wyjątki w Pythonie nie są rejestrowane, tzn. funk-cje i klasy nie określają, jakie wyjątki mogą rzucić. Ze względu na dynamiczną naturę języka bardzo trudne byłoby ograniczenie z góry szeregu wyjątków, które mogą się pojawić w trakcie wykonania dane-go kodu.

Wyjątek rzucamy, podając jego nazwę i po przecin-kach dodatkowe argumenty, np.:

>>> raise MemoryError, "I can't find my glasses!"

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

MemoryError: I can't find my glasses!

Kod, o którym wiemy, że z jakiegoś względu może rzucić wyjątek, możemy obwarować blokiem try:, except:, finally: np. w takiej sytuacji:

>>> try:

... import some_library

... except ImportError:

... print "some_library not installed."

... some_library not installed.

Często wyjątki zawierają kontekstowe dane, które możemy wykorzystać przy ich obsłudze. Żeby się do nich dostać, definiujemy w bloku except po przecinku nazwę zmiennej, pod którą chcemy mieć dostęp do obiektu wyjątku:

>>> guitarists = {'Oldfield': 'Mike', 'Santana':

'Carlos', 'Rea': 'Chris'}

>>> try:

... print guitarists['Malmsteen']

... except KeyError, e:

... print "%s is no guitarist." % e

...

'Malmsteen' is no guitarist.

Ostatecznie, istotną konwencją jest stosowanie blo-ków try:, finally: wszędzie tam, gdzie niezależnie od wyjątków musimy wykonać jakieś porządki po wy-konaniu bloku kodu. Przykładami takich sytuacji są najczęściej:

• potrzeba zamknięcia pliku • potrzeba zamknięcia połączenia do bazy • potrzeba zwolnienia pamięci lub miejsca na dysku • potrzeba zamknięcia połączenia sieciowego

W następnym odcinkuW przyszłym miesiącu, opierając się o naszą dzisiej-szą grubszą lekcję, odwiedzimy rejony programowa-nia obiektowego i innych przestarzałych paradygma-tów [3]. Przy okazji zabaw klasami zajmiemy się ma-nipulacją tekstem i liczbami. Poruszymy też kwestię operowania na plikach w Pythonie.

ŁUKASZ LANGA Programuje w Pythonie od 8 lat, aktualnie współpracuje z STX Next, rozwijając oprogramowanie dla sektora banko-wego. Prywatnie miłośnik muzyki fortepianowej, początku-jący ojciec i mąż. Adres e-mail autora: [email protected].

Przypisy• Świetny esej na ten temat napisał już w 1982 roku E. W. Dijkstra: http://userweb.cs.utexas.edu/users/EWD/transcriptions/

EWD08xx/EWD831.html• Tymczasem dla niecierpliwych: doskonała dokumentacja doctestów i tego, co można z nimi zrobić znajduje się tutaj: http:/

/docs.python.org/library/doctest.html• Pół żartem, pół serio. Python jest językiem łączącym kilka paradygmatów programowania, od strukturalnego, przez

obiektowe po funkcyjne. Zauważ, jak daleko udało nam się zabrnąć bez zdefiniowania ani jednej klasy. Ciekawą dys-kusję na temat konsekwencji takiej mieszanki funkcjonalności można znaleźć tu: http://lambda-the-ultimate.org/classic/message6153.html

PatronatPatronem cyklu artykułów poświęconych tematyce Py-thon jest firma STX Next (www.stxnext.pl). Celem całe-go cyklu jest popularyzacja języka Python, co jest wpi-sane w misję firmy. STX Next jest firmą typu software house, specjalizującą się w tworzeniu i wdrażaniu rozwią-zań internetowych klasy enterprise w oparciu o techno-logie Python, Plone, django, Pylons i pokrewne. W kolej-nych częściach zapoznamy czytelnika z możliwościami ję-zyka i biblioteki standardowej, natomiast w drugiej czę-ści cyklu przedstawimy najważniejsze frameworki, takie jak: Plone, django, Pylons, Turbogears, Repoze oraz Go-ogle App Engine.

Page 30: SDJ 08 2010

8/201030

PROGRAMOWANIE JAVA Certyfikat SCJP

www.sdjournal.org 31

W cyklu artykułów poświęconych przygotowa-niom do egzaminu 310-065 (Sun Certified Java Programmer for Java SE 6) przekroczy-

liśmy połowę kursu. W dzisiejszym odcinku powracamy do kanonu – po dłuższej „zabawie” z funkcjonalnością, czyli API (wejście/wyjście, łańcuchy znaków, wielowąt-kowość), czas wrócić do omawiania niuansów związa-nych stricte z językiem programowania.

Tematem tego artykułu będzie programowanie obiektowe w Javie ze wszystkimi (obowiązującymi na egzaminie) specyficznymi niuansami. Nim zanurzy-my się w otchłani Javowego kodu, nie mógłbym za-pomnieć o opisaniu krótkiego, acz istotnego (i często spotykanego na egzaminie) zagadnienia – powiązań i zwartości.

Flip i FlapHigh cohesion & Low coupling – wysoka zwartość i luź-ne powiązanie to dwa pojęcia, które wiążą się ściśle z projektowaniem klas w Javie i innych obiektowych ję-zykach programowania. Podczas egzaminu spotkasz się z dwoma rodzajami zadań, dotyczącymi tych zagad-nień. Przede wszystkim, musisz znać definicje tych po-jęć, warto także zapoznać się z kodami spełniającymi te dwie reguły. Nie pozostało mi nic innego, jak przedsta-wić dwa wyżej wspomniane terminy.

Wysoka zwartość obliguje programistę do tworzenia klas o ściśle określonej roli. Jeśli chcesz stosować się

do reguły wysokiej zwartości, nie powinieneś tworzyć klas takich, jak poniższa:

class Menadzer {

public void drukuj(String dane) {} // itd.

public int obliczParametr(int n, int m) {}

public InputStream pobierzStrumienSieciowy(String ip)

{}

}

Powyższa klasa przedstawia niską zwartość, ponie-waż zawiera funkcjonalność związaną z bardzo róż-nymi operacjami – drukuje dane, wykonuje oblicze-nia, realizuje operacje na gniazdach sieciowych. Jest to przykład klasy o zbyt rozmaitej funkcjonalności. Dobrze zaprojektowany system zawierałby osobne klasy (lub interfejsy), określające podane funkcjonal-ności osobno.

Luźne powiązanie między klasami oznacza, że te klasy są ze sobą powiązane najsłabiej, jak to tylko możliwe. Mówiąc językiem bardziej formalnym, klasy luźno powiązane powinny wzajemnie korzystać z jak najmniejszej liczby swoich metod, pól i innych możli-wych konstrukcji języka. Dotyczy to zwłaszcza wza-jemnych modyfikacji danych istniejących w obrębie obiektów (klas). Zbyt ścisłe powiązanie dwóch klas może doprowadzić do sytuacji, przedstawionej na Li-stingu 1.

Przewodnik po SCJP

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ę.

Dowiesz się:• Jak radzić sobie z nietypowymi konstrukcjami przy dziedzicze-

niu klas;• Jak unikać problemów z dostępnością klas i ich elementów;• Jak inne ważne zasady programowania obiektowego mogą

utrudnić Ci zdanie egzaminu.

Powinieneś wiedzieć:• Jak skompilować i uruchomić programy w Javie;• Jakie są podstawy składni języka;• Czym różni się klasa od interfejsu i obiektu.

czyli certyfikat z Javy – część 5

Page 31: SDJ 08 2010

8/201030

PROGRAMOWANIE JAVA Certyfikat SCJP

www.sdjournal.org 31

ten stanowi podklasę klasy C, a co za tym idzie – moż-na przypisać tak zrzutowaną referencję. Na etapie wy-konania programu referencja d, odwołująca się w isto-cie do obiektu typu B, zostanie zrzutowana na swój macierzysty typ, a zatem nie dojdzie do wyjątku klasy ClassCastException.

Nie zapominaj, że w tego typu zadaniach wyjątkowo potrafią zamieszać zwłaszcza interfejsy. W powyższym przykładzie w referencji d można umieścić odwołanie do obiektów klas B i A, ale klasy C – już nie!

Elementy statyczne vs. instancje – runda 2.O elementach (metodach i polach) statycznych by-ła już mowa w jednej z poprzednich części kursu, nie-mniej musimy omówić je po raz kolejny (wraz z meto-dami i polami instancji), tym razem w kontekście dzie-dziczenia. Jednym z większych problemów, występu-jących w zadaniach egzaminacyjnych, jest połączenie zagadnienia przeciążania i przesłaniania metod z mo-dyfikatorem static, a także z modyfikatorami widocz-ności (public, protected, itd.). Rozważmy kod umiesz-czony na Listingu 2.

Która z metod print() zostanie wywołana? Czy me-toda statyczna z klasy bazowej ma przewagę nad me-todą tradycyjną, jednak zadeklarowaną w klasie bieżą-cej? Obydwa podejścia pogodzi błąd kompilacji. Nie wolno przesłaniać wzajemnie metod statycznych i in-stancji.

To było proste. Przesłanianie metod instancji było już omawiane, przejdziemy więc do przesłaniania me-tod statycznych. Załóżmy więc, że zarówno metoda print() z klasy A, jak i B, są statyczne. Co wydarzy się w momencie poniższych wywołań?

A a = new A();

a.print();

// lub

B b = new B();

b.print();

Rzutowanie, dziedziczenie i polimorfizmW tradycyjnych aspektach programowania obiektowego w Javie tkwi całkiem spore pole do popisu dla autorów zadań egzaminacyjnych. Na egzaminie niezbędna jest praktyczna znajomość polimorfizmu. Typowe zadania eg-zaminacyjne, sprawdzające wiedzę na temat polimorfi-zmu, zawierają ogromną liczbę rzutowań – zarówno tych sensownych, podchwytliwych, jak i całkiem głupich.

Na początek – przyzwyczaj się do nietypowych, po-gmatwanych hierarchii klas, np. B dziedziczy po A, A dziedziczy po C, a dodatkowo A implementuje interfejs D. Po utworzeniu kilku obiektów (w sposób tradycyjny, np. B b = new B();) musisz wiedzieć, które przypisania (rzutowania) mogą zaistnieć w danym przykładzie, np.:

C c = new C();

D d = new B();

C c = d;

Powyższy kod (przy założeniu, że obowiązują opisane wcześniej relacje między klasami a interfejsem) nawet się nie skompiluje! Problem stanowi wiersz 3. Mimo że referencja d zawiera obiekt klasy B (który dzięki dzie-dziczeniu jest też obiektem klasy C), nie może być ona bezpośrednio przypisana do referencji klasy C. Kompi-lator nie wie, co w momencie wykonania programu bę-dzie znajdować się w referencji. Może więc polegać je-dynie na typie referencji. Tymczasem interfejs D nie ma żadnego związku z klasą C. Z tego względu kompilacja nie powiedzie się. Zupełnie inaczej będzie wyglądać sytuacja, jeśli zmienisz kod na poniższy:

C c = new C();

D d = new B();

C c = (B)d;

W tym przypadku zarówno kompilacja, jak i wykona-nie powyższego kodu powiodą się. Z punktu widzenia kompilatora, referencja d jest rzutowana na typ B. Typ

Listing 1. Przykład zbyt mocno powiązanych ze sobą klas

class DataHandler {

InputStream inp;

public DataHandler() {} // tu otwieranie strumienia

public byte[] loadData() {} // tu zwracanie danych

}

class FalseBusinessLogic {

public String createBusinessData(DataHandler dh) {

// byte[] data = dh.loadData(); - tak powinno być

byte[] bufor = new byte[1024];

dh.inp.read(bufor, 0, bufor.length);// taki kod się skompiluje, ale nie jest to luźne powiązanie!

} // metoda biznesowa

}

Page 32: SDJ 08 2010

8/201032

PROGRAMOWANIE JAVA Certyfikat SCJP

www.sdjournal.org 33

Pierwszy z dwuwierszy wyświetli wartość 1, a dru-gi – 2. Nic to – cytując klasyka – przecież w obu przy-padkach korzystamy wyraźnie z odpowiednich klas. Co jednak stanie się w kolejnej sytuacji?

B b = new B();

((A)b).print();

W tej sytuacji dysponujemy obiektem klasy B (referencja również jest tego typu), jednak tuż przed wywołaniem metody rzutujemy referencję na typ A. To właśnie ta operacja okazuje się być decydującą. Po-wyższy kod wyświetli wartość 1! Wynika to z prostej reguły – w przypadku przesłaniania metod statycz-nych wybór metody następuje na etapie kompilacji, a w przypadku metod instancji – na etapie wykonania.

Zwróć uwagę, że metody statyczne mogą być bez problemu wywoływane za pomocą obiektów danej kla-sy, a ściśle rzecz mówiąc – referencji do nich. Nie na-leży jednak przywiązywać do referencji zbytniej wa-gi – z punktu widzenia maszyny wirtualnej referencja informuje jedynie o typie, z jakiego należy skorzystać podczas wywołania metody statycznej. Co za tym idzie, kod przedstawiony na Listingu 3 jest poprawny!

Mimo że wyrażenie C.getC() zostaje sprowadzone do wartości null, maszyna wirtualna wie, jakiego typu war-tość jest zwracana przez metodę getC(). Dzięki temu wiemy, z jaką klasą mamy do czynienia i możemy wy-wołać jej metodę print().

Wyższy poziom wtajemniczenia – przesłanianie + konstruktoryDo panteonu obiektowych kruczków pojawiających się na egzaminie SCJP należy dołączyć problemy z kon-struktorami. Zagadnienia omawiane w niniejszym aka-picie nie są zbyt wyszukane – najczęściej wystarczy

zachować zimną krew, opanować oczopląs i spokojnie przeanalizować treść załączonych kodów źródłowych. Lepiej jednak dmuchać na zimne, dlatego kilka typo-wych przykładów omówimy już teraz.

Poza typowo zaciemniającymi kod praktykami, pro-blem z konstruktorami wynika głównie z zachowania kompilatora. Przypomnijmy sobie jedną z ważnych re-guł dotyczących konstruktorów – jeśli w danej klasie konstruktor nie został zadeklarowany, kompilator au-tomatycznie dołączy definicję konstruktora bezparame-trycznego, publicznego i zawierającego wywołanie bez-parametrycznego konstruktora z klasy nadrzędnej (to prawda, trochę to skomplikowane).

Typowy przykład takiego kodu prezentuję poniżej. Te-go typu sytuacje mogą nawet mieć miejsce w praktyce – wystarczy zapomnieć o zadeklarowaniu konstrukto-ra domyślnego (oczywiście przy deklaracji także innych konstruktorów), a następnie zadeklarować podklasę, zapominając o umieszczeniu odpowiedniego wywoła-nia konstruktora za pomocą słowa super:

class A {

public A(int x) {}

}

class B extends A {

public B(int y) {}

}

Błąd tkwi oczywiście w domyślnym i zarazem nie-jawnym zachowaniu kompilatora. Reguła mówi: każ-dy konstruktor musi zawierać (jako pierwszą instruk-cję) odwołanie do dowolnego konstruktora klasy nad-rzędnej lub innego konstruktora tej samej klasy (np. this(x, y);). W przypadku pominięcia tej instrukcji, kompilator automatycznie wstawi wywołanie super();. Zazwyczaj nie stanowi to problemu, jednak jeśli zade-klarujesz jakiekolwiek konstruktory w klasie nadrzęd-nej (tak jak w klasie A) i zarazem zapomnisz umieścić Listing 2. Metoda statyczna vs. niestatyczna – odsłona

pierwsza

class A {

public static void print() {

System.out.println("1");

}

}

class B extends A{

public void print() {

System.out.println("2");

}

public static void main(String[] args) {

B b = new B();

b.print();

}

}

Listing 3. Specy�czny przykład działania metod statycznych

class C {

public static C getC() {

return null;

}

public static void print() {

System.out.println("test");

}

}

class D {

public static void main(String[] args) {

C.getC().print();

}

}

Page 33: SDJ 08 2010

8/201032

PROGRAMOWANIE JAVA Certyfikat SCJP

www.sdjournal.org 33

wywołania w kodzie konstruktora klasy podrzędnej, powstanie błąd. Powyższy kod można naprawić, zmie-niając treść konstruktora klasy B:

public B(int y) { super(y); }

Oczywiście konkretne rozwiązanie zależy od tego, co faktycznie chcesz osiągnąć. Na egzaminie możesz się spodziewać jedynie wskazania problemu i podania do-wolnego poprawnego rozwiązania.

Przy okazji konstruktorów i przesłaniania warto wspo-mnieć o problemie nieco bardziej ogólnym, niemniej równie często występującym na egzaminie. Chodzi o znane i (nie)lubiane modyfikatory dostępu. Rozpatrz-my poprzednie fragmenty kodu właśnie pod kątem mo-dyfikatorów. Co sądzisz o poniższym kodzie?

class A {

A(int x) {}

}

class B extends A {

B(int y) { super(y); }

}

Jest to kod niewątpliwie poprawny. W porównaniu do poprzedniej wersji zmieniliśmy modyfikatory dostę-pu z public na domyślny, czyli na modyfikator pakie-tu (dostęp do elementu jedynie w obrębie pakietu). Nie muszę chyba mówić, co się stanie, jeśli powyższy kod zostanie rozdzielony do dwóch pakietów:

package a; // plik A.java

class A {

A(int x) {}

}

package b; // plik B.java

import a.A;

class B extends A {

B(int y) { super(y); }

}

Podział kodu na dwa pliki, i co ważniejsze, dwa pakie-ty, spowoduje, że konstruktor klasy A nie jest widoczny w konstruktorze klasy B.

Być czy mieć?Ostatnie podzagadnienie określone w ramach zagad-nienia piątego wymaga znajomości relacji IS-A oraz HAS-A. Jest to jedno z podstawowych zagadnień pro-gramowania obiektowego i nie wątpię, że większość

Czytelników spotkała się z tymi terminami; może nieko-niecznie pod taką nazwą.

Relacja IS-A określa pokrewieństwo między klasami zgodnie z regułami dziedziczenia. Obiekt klasy B jest także obiektem klasy A (B IS-A A), jeśli spełniona jest jedna z zależności:

class A {}

class B extends A {}

// lub

interface A {}

class B implements A {}

Oczywiście zależność między klasami nie mu-si być bezpośrednia. W poniższym przykładzie klasa C również spełnia relację IS-A w odniesieniu do klasy A:

class A {}

class B extends A {}

class C extends B {}

// lub

interface A {}

interface B extends A {}

class C implements B {}

Relacja HAS-A określa związek zawierania. Klasa A pozostaje z klasą B w relacji zawierania, jeśli spełnio-na jest zależność:

class A {

B b;

}

Konieczne jest więc przechowywanie referencji do da-nego obiektu klasy.

Typowe zadania, związane zarówno z relacją IS-A, jak i HAS-A, dotyczą określenia relacji pomiędzy klasa-mi (interfejsami) na podstawie kodu lub wskazania po-prawności podanych zdań. Znajomość obu konceptów w zupełności wystarcza do poprawnego rozwiązywania tego typu zadań.

Na tym niewątpliwie prostym zagadnieniu kończymy piątą część kursu SCJP. W części szóstej jedno z naj-ważniejszych, zarówno na egzaminie, jak i w praktyce programistycznej, zagadnienie (a właściwie zagadnie-nia) – duet kolekcje & generyki.

KRZYSZTOF RYCHLICKI KICIORAutor programuje w Javie i .NET. Ostatnio zaintrygowało go połączenie Adobe Flex z J2EE, czyli LiveCycle Data Services. Pisze książki i artykuły na różne tematy związane z progra-mowaniem. Jest posiadaczem certy�katu SCJP i SCWCD; od kwietnia 2007 do kwietnia 2008 legitymował się tytułem Mi-crosoft MVP w kategorii Visual C#. Kontakt z autorem: [email protected]

BibliografiaSierra K., Bates B. – Sun Certi�ed Programmer for Java 5 Stu-dy Guide

Page 34: SDJ 08 2010

8/201034

PROGRAMOWANIE JAVA

Pytania1. W jednej z klas aplikacji pojawia się następujący kod:

class Okno { // reprezentuje kontrolkę graficzną okno public void wyswietl(String sciezka) throws

IOException{ FileInputStream fis = new FileInputStream(sciezka); byte[] bufor = new byte[1024]; fis.read(bufor); fis.close(); // dalsze operacje z wykorzystaniem bufora } }

Które z poniższych określeń pasuje do powyższego kodu?a) Wysoka zwartośćb) Niska zwartośćc) Silne powiązaniad) Luźne powiązania

2. Którą z podanych instrukcji można wstawić w miejsce oznaczone komentarzem //1, aby kod skompilował się i program nie zwrócił wyjątku rzutowania?

class A { } class B extends A { } interface A1 {} interface A2 extends A1 {} class C extends B implements A1 {} public class Main { public static void main(String[] args) { A a = new A(); A1 a1 = new C(); // 1 } }

a) A1 a1 = new B();b) A2 a = new A();c) A a = new C();d) A2 a2 = (A2)a1;e) A1 a1 = a;f) B b = (B)a1;

3. Jaki efekt spowoduje wywołanie poniższego kodu?

public class Main { public static void main(String[] args) { C c = new C(); } }

class A { public static void A(int x) { System.out.print("Klasa A, "); } } class B extends A { public B() { System.out.print("Konstruktor B, "); } } class C extends B { C() { System.out.print("Konstruktor C, "); } }

a) kod nie skompiluje sięb) kod skompiluje się, jeśli zostanie dodany modyfikator

public w konstruktorze klasy Cc) kod skompiluje się i program wyświetli tekst Konstruktor B,

Konstruktor Cd) kod skompiluje się i program wyświetli tekst Konstruktor C,

Konstruktor B

4. Które z poniższych zdań, dotyczących poniższego kodu, jest prawdziwe?

class A { A(int x) {} private A(Integer x) {} } class B extends A { B() { this(3); } B(int z) { super(z); } } public class Main { public static void main(String[] args) { B b = new B(); } }

a) kod nie skompiluje sięb) kod skompiluje się wtedy, i tylko wtedy, gdy zostanie usu-

nięte słowo privatec) kod skompiluje się wtedy, i tylko wtedy, gdy pliki będą znaj-

dowały się w tym samym pakiecied) kod skompiluje się w takiej postaci, w jakiej jest

5. Które z poniższych zdań są prawdziwe?a) Komputer zawiera procesorb) Komputer jest urządzeniemc) Komputer zawiera urządzenied) Komputer jest procesorem

Odpowiedzi:1. b, c – klasa nie jest zwarta, ponieważ realizuje operacje na danych (operacje wejścia/wyjścia), mimo że jest klasą odpowiedzialną za operacje graficzne. Sam

fakt wykorzystywania w tak dużym stopniu klasy odpowiedzialnej za operacje (I/O) niezwiązane z tą klasą świadczy o silnych powiązaniach.2. f – większość przykładów (poza d i f) nie skompiluje się, zaś przykład d zwróci wyjątek ClassCastException .3. c – Java nie zabrania tworzenia metod, o tej samej nazwie, co klasa. Z tego względu istnienie metody public static void A() nie zakłóca działania

przykładu. Również modyfikatory są poprawne. Kolejność wyświetlanych tekstów jest związana z automatycznym wstawianiem wywołania konstruktora kla-sy bazowej – super().

4. d – Kod jest poprawny. Wariant c byłby poprawny, gdyby nie sformułowanie wtedy, i tylko wtedy, gdy – na egzaminie należy uważać na odpowiedzi poprawne merytorycznie, które jednak wykluczają inne, również prawidłowe, odpowiedzi.

5. a, b, c – Wariant a jest oczywisty. Wariant b – klasa Komputer może dziedziczyć po klasie Urzadzenie , tak jak np. klasy Drukarka , Telewizor, etc. Wariant c jest najbardziej interesujący – zasilacz, napęd DVD czy nawet PC speaker możemy potraktować jako proste urządzenia – w takim rozumieniu komputer rów-nież zawiera urządzenia.

Page 36: SDJ 08 2010

8/201036

PROGRAMOWANIE JAVA Java na BlackBerry

www.sdjournal.org 37

BlackBerry jest rodziną telefonów komórkowych zaliczanych do segmentu tzw. smartphone'ów. Jest to produkt kanadyjskiej firmy Research In

Motion produkowany już od 11 lat. Cechą charaktery-styczną urządzeń BlackBerry jest łatwa obsługa wia-domości e-mail. Stoi za tym technologia „push-mail”, polegająca na współpracy terminali (telefony komór-kowe) z serwerami pocztowymi BlackBerry. Współ-praca ta przejawia się w „wypychaniu” emaili z serwe-ra na terminale, co daje możliwość odbioru poczty po-dobnie jak w przypadku odbioru sms'ów. Funkcjonal-ność ta stała się znakiem firmowym BlackBerry i w du-żym stopniu przyczyniła się do sukcesu rynkowego w USA. W Europie technologia ta jest mniej popular-na. W USA natomiast udziały firmy RiM w rynku smart-phon'ów wynoszą ponad 50%. Drugie miejsce zajmu-je dopiero iPhone.

BlackBerry i JavaBlackBerry ma długą historię współpracy z technolo-gią Java. Od modelu 5810 (2002 rok) większość co-re’owych aplikacji jest napisanych w tym języku. Pro-ducent telefonów stworzył również API, przy pomo-cy którego każdy programista może tworzyć aplikacje w języku Java. Najnowsza wersja API (ver. 5.0) udo-stępnia zaawansowany zestaw funkcjonalności. De-veloperzy mogą programować obsługę m.in. GPS,

bluetooth, mają szeroki wybór komponentów do two-rzenia interfejsu użytkownika. Platforma BlackBer-ry oferuje również trwałe źródła danych, w związku z czym możliwy jest zapis danych. To wszystko spra-wia, że bardzo proste staje się rozwijanie aplikacji wy-posażonych w najbardziej zaawansowane funkcje i odpowiadających najbardziej wyszukanym gustom użytkowników.

Sposoby tworzenia aplikacjiPlatforma BlackBerry umożliwia tworzenie aplikacji w Javie na dwa sposoby. Pierwszy, o którym wspo-mnieliśmy wyżej, polega na korzystaniu z wbudowa-nego API i tworzeniu aplikacji uruchamianych bezpo-średnio na platformie BlackBerry (na systemie opera-cyjnym). Drugi sposób wiąże się z rozwojem aplikacji webowych. Wykorzystuje się tu możliwości wbudowa-nej przeglądarki internetowej. Sam proces tworzenia aplikacji webowych przebiega identycznie jak w przy-padku tworzenia aplikacji pod zwykłe przeglądarki stron internetowych, czyli wykorzystuje się tu techno-logie takie jak: html, css itd.

Twórcy oprogramowania nie mogą narzekać na ze-staw narzędzi developerskich dostarczonych przez firmę RiM. Zarówno proces budowania aplikacji bez-pośrednio na platformę BlackBerry, jak i tzw. web de-velopment mają swoje wsparcie w odpowiednich na-

Java na BlackBerry

Artykuł przedstawia podstawy programowania aplikacji w języku Java pod system BlackBerry. W podstawowym zakresie omówione zostały cztery tematy: ogólne sposoby tworzenia aplikacji, budowanie interfejsu użytkownika, programowanie menu telefonu oraz zagadnienie utrwalania danych.

Dowiesz się:• Jakie są sposoby tworzenia aplikacji w języku Java na telefony

BlackBerry;• Jak tworzyć komponenty interfejsu użytkownika i rozmiesz-

czać je na ekranie;• Jak dodawać elementy do Menu telefonu;• Jak zapisywać i odczytywać dane.

Powinieneś wiedzieć:• Podstawowa znajomość języka Java;• Znajomość Eclipse IDE;• Znajomość standardu J2Me może być pomocna.

Podstawy pisania aplikacji

Page 37: SDJ 08 2010

8/201036

PROGRAMOWANIE JAVA Java na BlackBerry

www.sdjournal.org 37

W przeciwieństwie do np. platformy Android jest możliwe jedynie programowalne definiowanie UI, de-klaratywne (ustawianie komponentów UI w np. pli-kach xml) nie jest obsługiwane. Jest to z pewno-ścią pewna luka w API. Pozycjonowanie elementów UI w odseparowanych plikach jest rozwijającym się trendem w programowaniu interfejsów użytkownika.

Tworzenie GUI przebiega więc podobnie jak w przy-padku programowania z użyciem biblioteki Swing z podstawowego pakietu Javy (J2SE). Klasa każde-go komponentu jest tworzona „ręcznie” przez progra-mistę, zwykle w konstruktorach lub blokach statycz-nych.

rzędziach. Są to wtyczki do popularnych środowisk developerskich (IDE), np. do Eclipse'a, lub też cał-kiem odrębne środowiska programistyczne stworzone od podstaw przez producenta.

Istotnym udogodnieniem jest także liczna gama emulatorów poszczególnych modeli telefonów Black-Berry. Po instalacji Java Development Environment (JDE) w którejkolwiek wersji, instalowane są domyśl-ne emulatory. Użytkownik może jednak ściągnąć i za-instalować emulator praktycznie każdego dostępne-go na rynku telefonu BlackBerry. Trzeba jednak przy-znać, że emulatory nowszych modeli działają dość wolno, stąd też do developmentu wygodne jest korzy-stanie ze starszych modeli.

BlackBerry JDEW artykule zajmiemy się tylko pierwszym sposobem tworzenia aplikacji. Będziemy korzystać z wtyczki do środowiska Eclipse (BlackBerry Java Plug-in for Eclip-se v1.1) oraz z Java Development Environment w wer-sji 4.5 (JDE 4.5). Wskazanie wszystkich elementów potrzebnych do konfiguracji całego środowiska develo-perskiego BlackBerry znajduje się w ramce „W sieci”.

Prosta aplikacjaZakładamy, że czytelnik posiada poprawnie zainstalo-wane JDE, plugin do Eclipse'a oraz emulator. Zajmie-my się więc utworzeniem prostej aplikacji będącej for-mą notatnika. Z racji tego, że artykuł obejmuje pod-stawy programowania na platformie BlackBerry, w ilu-strującej go aplikacji zastosowaliśmy dość stare JDE w wersji 4.5. Jednakże zawarte tam API w pełni za-spokaja potrzeby tak prostego programu.

W procesie tworzenia aplikacji poruszymy trzy za-gadnienia. Pierwszym będzie programowanie pro-stego interfejsu użytkownika (UI), drugim – zaimple-mentowanie jednej funkcji w „Menu” telefonu, trzecim – oprogramowanie zapisu danych wprowadzonych przez użytkownika do trwałego źródła.

Na platformie BlackBerry każda klasa generują-ca interfejs użytkownika musi dziedziczyć z klasy UIApplication. Klasa ta zawiera metody powołujące do życia m.in. układ wszystkich elementów UI. Taką metodą jest pushScreen(). W naszej aplikacji główną klasą jest Notepad (Listing 1).

Jak widać, punktem startowym aplikacji jest meto-da main(), w której tworzony jest obiekt klasy Notepad i tym samym inicjalizowana jest cała aplikacja.

Tworzenie interfejsu użytkownikaBlackBerry API udostępnia szeroki wachlarz kompo-nentów służących do budowania interfejsu użytkowni-ka. Klasy komponentów znajdują się w pakiecie

net.rim.device.api.ui.

Listing 1. Główna klasa przykładowej aplikacji

public class Notepad extends UiApplication {

public Notepad() {

pushScreen(new UICreator().createMainScreen()

);

}

public static void main(String[] args) {

Notepad notepad = new Notepad();

notepad.enterEventDispatcher();

}

}

Listing 2. Metoda createMainScreen() tworząca układ i wygląd głównego ekranu aplikacji

public MainScreen createMainScreen() {

initMainScreenAndComponents();

addComponentsToMainLayoutManager();

addLayoutManagerToMainScreen();

return mainScreen;

}

Listing 3. Inicjalizacja VerticalFieldManagera oraz dodawanie do niego komponentów

private void addComponentsToMainLayoutManager() {

mainVfm = new VerticalFieldManager(Manager.VERTI

CAL_SCROLL);

mainVfm.setMargin(10, 0, 0, 10);

mainVfm.add(notesText);

mainVfm.add(saveButton);

mainVfm.add(vfm);

}

Page 38: SDJ 08 2010

8/201038

PROGRAMOWANIE JAVA Java na BlackBerry

www.sdjournal.org 39

W naszej przykładowej aplikacji za zarządzanie komponentami UI odpowiada klasa UICreator. Kla-sa ta posiada pola będące referencjami do poszcze-gólnych komponentów oraz jedną metodę publiczną createMainScreen() służącą do stworzenia głównego ekranu aplikacji. Metoda ta przedstawiona jest na Li-stingu 2.

Z klasy UICreator korzysta główna klasa aplikacji Notepad.

Jak każde API dostarczające komponentów do two-rzenia UI, platforma BlackBerry zawiera komponen-ty do rozmieszczania innych komponentów na ekra-nie. Są to tzw. managery (layout managers) i w naszej aplikacji wykorzystamy dwa ich podstawowe rodzaje. Będzie to HorizontalFieldManager (jak sama nazwa wskazuje, rozmieszcza elementy poziomo) i Verti-calFieldManager (rozmieszcza pionowo). Spójrzmy na fragment kodu, gdzie wykorzystane jest użycie tych managerów (Listing 3).

Listing 3 pokazuje inicjalizację głównego manage-ra layoutu zawierającego wszystkie pozostałe kompo-nenty graficznego interfejsu użytkownika.

Użyta stała Manager.VERTICAL_SCROLL powoduje po-jawienie się paska przewijania w sytuacji, gdy wy-świetlane dane nie mieszczą się na ekranie.

Ustawiony margines mainVfm.setMargin(10, 0, 0, 10); oznacza przesunięcie całej zawartości ekranu w pionie i poziomie o 10 pikseli, tak aby komponenty UI nie były wyświetlane tuż przy krawędzi ekranu.

Lista dostępnych komponentów interfejsu użytkow-nika jest podobna do tej, którą oferują konkurencyjne platformy (Android OS). Znajdziemy tam: komponen-ty odpowiedzialne za przyciski (buttony), pola teksto-we, listy rozwijane, tzw. radio buttony, check-boxy i in-ne. Na Listingu 4 pokazujemy inicjalizację większości komponentów użytych w aplikacji.

Z listingu tego widać podobieństwa do biblioteki Swing. Akcje wykonywane po naciśnięciu komponentu ButtonField (przycisk) obsługują listenery. W tym kon-kretnym przypadku jest to FieldChangeListener.

Występujący w prawie każdej bibliotece GUI ele-ment Dialog odpowiedzialny jest za wyświetlenie okna dialogowego z odpowiednim komunikatem. Jego rola na platformie BlackBerry jest taka sama.

Z Listingu 3 i Listingu 4 wyłania się powoli cało-kształt prostego interfejsu użytkownika. Głównym kon-

Rysunek 1. Ogólny widok interfejsu użytkownika

Listing 4. Inicjalizacja VerticalFieldManagera oraz dodawanie do niego komponentów

private void initMainScreenAndComponents() {

mainScreen = new MainScreen();

mainScreen.setTitle(new LabelField("Notatnik"));

notesText = new EditField("Notatka: ", "");

saveButton = new ButtonField("Zapisz");

saveButton.setChangeListener(new

FieldChangeListener() {

public void fieldChanged(Field field, int

context) {

NotesInfo notesInfo = new NotesInfo();

notesInfo.setElement(notesText.getText());

pm.persist(notesInfo);

Dialog.inform("Zapisano!");

notesText.setText(null);

}

});

vfm = new VerticalFieldManager();

}

Page 39: SDJ 08 2010

8/201038

PROGRAMOWANIE JAVA Java na BlackBerry

www.sdjournal.org 39

tenerem jest VerticalFieldManager, który posiada trzy elementy: pole tekstowe z opisem, button do zapisu notatki oraz zagnieżdżony VerticalFieldManager słu-żący do wyświetlenia w formie pionowej listy zacią-gniętych ze źródła danych notatek. Efekt tych usta-wień widać na Rysunku 1.

Menu telefonuProgramowanie aplikacji na smartphony bardzo czę-

sto wiąże się z koniecznością zaprogramowania tzw. menu telefonu, mającego najczęściej postać listy roz-wijanej pokazującej się z boku ekranu. Również i plat-forma BlackBerry posiada API pozwalające na stwo-rzenie menu. W naszej aplikacji za obsługę menu odpowiada klasa UICreator. Posiada ona pole typu MainItem odpowiadające za stworzenie jednego ele-mentu w menu telefonu. Na Listingu 5 jest przedsta-wiony kod tej klasy.

Możemy tam znaleźć kilka interesujących fragmen-tów kodu. Kod w metodzie run() wykonuje się po wy-braniu pozycji w menu.

Zmienna pm jest referencją do PersistentManagera, klasy omówionej dokładnie w dalszej części arty-kułu, a służącej do zarządzania trwałością obiek-tów (notatek). Widzimy tam zaciągnięcie ze źró-dła danych wszystkich dotychczas zapisanych nota-tek, wykonanie iteracji, w końcu wyświetlenie nota-tek na ekranie po uprzednim umiejscowieniu ich w

Rysunek 2. Menu telefonu z opcją „Pobierz notatki”

Listing 5. Komponent MenuItem tworzący element w menu: „Pobierz Notatki”

private MenuItem getItem = new MenuItem("Pobierz

notatki", 110, 11) {

public void run() {

Vector data = pm.load();

boolean dataWasLoaded = !data.isEmpty();

if (dataWasLoaded) {

displayNotes(data);

}

}

private void displayNotes(Vector data) {

Enumeration enumeration = data.elements();

vfm.deleteAll();

index = 1;

while (enumeration.hasMoreElements()) {

putNotesToLayoutManager(enumeration);

}

notesText.setText(null);

}

private void putNotesToLayoutManager(Enumeratio

n enumeration) {

NotesInfo nf = (NotesInfo) enumeration.next

Element();

hfm = new HorizontalFieldManager();

hfm.setMargin(10, 0, 0, 0);

hfm.add(new LabelField(index++ + "."));

hfm.add(new LabelField(nf.getElement()));

vfm.add(hfm);

}

};

}

Page 40: SDJ 08 2010

8/201040

PROGRAMOWANIE JAVA

www.sdjournal.org

HorizontalFieldManager. Te zadania oczywiście reali-zują dwie prywatne funkcje displayNotes(Vector data) i putNotesToLayoutManager(Enumeration enumeration). Na Rysunku 2 jest przedstawiony widok aplikacji z rozwiniętym menu telefonu.

Zapis danychJedną z podstawowych funkcji praktycznie każdej aplikacji jest przechowywanie danych, na których aplikacja działa. BlackBerry umożliwia zapis danych w systemie i odtwarzanie ich zarówno po ponownym uruchomieniu aplikacji, jak i po restarcie całego sys-temu operacyjnego (restart telefonu). Od wersji JDE 5.0 istnieje możliwość zapisu danych w bazie danych SQLite. Ten niewielki silnik bazodanowy znany jest także m.in. z systemu Android i świetnie sprawdza się w zastosowaniach mobilnych.

Na platformie BlackBerry, każdy obiekt, któ-ry chcemy zapisać, musi implementować interfejs Persistable. W naszym projekcie klasą encji jest NotesInfo przechowująca informację o zapisanym tekście notatki. Na Listingu 6, z racji jego niewielkiej objętości, umieściliśmy kod całej klasy.

Oczywiście klasa ta opakowuje kilka funkcjonalno-ści. Na przykład zmienna typu Vector może służyć do przechowywania kilku właściwości danej notatki. W naszym przykładzie korzystamy jednak tylko z za-pisu jednej właściwości – samego tekstu notatki.

Samą logiką zapisu zajmuje się stworzona przez nas klasa PersistentManager. Zawiera ona od-wołanie do dostarczonej przez BlackBerry klasy PersistentObject. Stanowi ona faktyczne opakowa-nie dostępu do źródła danych i interfejs komunika-cji z nim. Sam obiekt uzyskuje się, korzystając z klas API: PersistentObject store = PersistentStore.getPersistentObject(0xdec6a67096f133cL);

W metodzie getPersistentObject(long key) użyliśmy dedykowanego klucza identyfikującego. Każdy obiekt zapisywany do źródła danych (czyli w naszym przypad-ku – NotesInfo) musi mieć unikalny klucz jednoznacz-nie identyfikujący go w źródle danych i pozwalający na wykonywanie na nim akcji zapisu lub pobrania.

Bazując na tych założeniach, utworzyliśmy dwie me-tody w PersistentManager – jedną do zapisu notatki, drugą do jej odczytu. Pokazane jest to na Listingu 7.

W Sieci• Eclipse IDE do ściągnięcia: http://www.eclipse.org/downloads/• BlackBerry Plugin do Eclipse'a: http://na.blackberry.com/eng/developers/javaappdev/devtools.jsp• BlackBerry JDE – różne wersje: http://na.blackberry.com/eng/developers/javaappdev/javadevenv.jsp• Dostępne emulatory: http://na.blackberry.com/eng/developers/resources/simulators.jsp• Opis instalacji i konfiguracji środowiska developerskiego: http://na.blackberry.com/developers/resources/A1_Setting_up_neces-

sary_tools_v5.0.pdf• Dokumentacja BlackBerry API ver. 4.5 (JavaDoc): http://www.blackberry.com/developers/docs/4.5.0api/index.html

Listing 6. Klasa jedynej encji w aplikacji - NotesInfo

public final class NotesInfo implements Persistable {

public static final int ID = 0;

public static final int NAME = 1;

private Vector elements;

public NotesInfo() {

elements = new Vector(1);

for (int i = 0; i < elements.capacity(); ++i) {

elements.addElement(new String(""));

}

}

public String getElement() {

return (String) elements.elementAt(NAME);

}

public void setElement(String value) {

elements.addElement(value);

}

}

Listing 7. Metody do zapisu i odczytu notatek ze źródła danych

public void persist(NotesInfo notesInfo) {

data.addElement(notesInfo);

synchronized (store) {

store.setContents(data);

store.commit();

}

}

public Vector load() {

synchronized (store) {

data = (Vector) store.getContents();

}

return data;

}

Page 41: SDJ 08 2010

8/201040

PROGRAMOWANIE JAVA

www.sdjournal.org

Wykorzystane są tu metody PersistentObject do ustawienia i komitowania danych (setContents() i commit()) oraz do zaciągnięcia wcześniej zapisa-nych danych (getContents()).

Metody te używamy w odpowiednich akcjach wy-woływanych na zdarzenie kliknięcia na ButtonField lub element menu. W pierwszym przypadku kod per-systencji danych (pm.persist(data);) znajduje się w metodzie listenera i jest widoczny na Listingu 4. Kod odczytu (Vector data = pm.load();) jest umiej-scowiony w metodzie run() klasy MenuItem, co wcze-śniej już pokazaliśmy na Listingu 5.

Krótkie podsumowaniePlatforma BlackBerry oferuje programistom wygodny interfejs do tworzenia aplikacji. Można tak powiedzieć już o JDE w wersji 4.5, w której utworzyliśmy prostą aplikację. Pełnię możliwości widać jednak w JDE 5.0. BlackBerry API z pewnością zostanie jeszcze rozsze-rzona w wersji 6.0 (firma RiM wypuściła bowiem w ro-ku 2010 wersję 6.0 systemu operacyjnego).

Sam sposób tworzenia aplikacji bardziej przypomina programowanie w standardzie J2Me. Architektura apli-kacji różni się znacznie od np. coraz bardziej popular-nej platformy Android OS. Nie ma wsparcia dla dekla-ratywnego definiowania interfejsu użytkownika. Z dru-giej strony, od najwcześniejszych wersji systemu i JDE występowało wsparcie dla bluetooth'a czym nie może pochwalić się Google Android. Oczywiście API umoż-liwia korzystanie z funkcjonalności charakterystycz-nych dla telefonów BlackBerry, takich jak push data. Dostępne jest również oprogramowanie usług takich jak GPS, bluetooth, PIM (Personal Information Mana-gement), Mobile Media API i inne.

Najciekawszym, zwłaszcza dla developerów, zagad-nieniem jest możliwość umieszczania swoich aplikacji w internetowym serwisie (sklepie) oraz ich sprzedaż. Podobne rozwiązania posiadają konkurencyjne plat-formy iPhone i Android. Platformą sprzedaży w przy-padku BlackBerry jest BlackBerry App World zawiera-jąca aplikacje darmowe i płatne z przeróżnych katego-rii. Wydaje się, że rozbudowane API, prosty sposób budowania aplikacji w połączeniu z możliwością ich zarobkowej sprzedaży jest wystarczającą zachętą do rozpoczęcia pisania własnych programów.

TOMASZ MILCZAREK,Prowadzi własną �rmę JCode. Pracuje jako konsultant Java EE przy projektach głównie z sektora telekomunikacyjnego. Oprócz Javy korporacyjnej w wolnych chwilach rozwija swoje umiejętności z zakresu technologii mobilnych. W szczególnym centrum jego zainteresowań znajdują się Google Android OS i platforma BlackBerry.Kontakt z autorem: [email protected]

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

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

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

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

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

Page 42: SDJ 08 2010

8/201042

PROGRAMOWANIE JAVA Wiosna z drugą twarzą w chmurach – część I

www.sdjournal.org 43

Artykuł jest pierwszym z serii artykułów prezen-tujących kompletny proces wytwarzania apli-kacji webowej w technologii Java Server Fa-

ces (JSF) w wersji 2.0. Założeniem autora jest poka-zanie tego procesu od podstaw wraz ze wszystkimi wymaganymi krokami w taki sposób, aby Czytelnik mógł wykonać je samodzielnie. Czytelnik będzie śle-dził kolejne etapy powstawania aplikacji – na każdym

etapie autor będzie dorzucał kolejną „cegiełkę”, aby finalnie otrzymać gotową w pełni funkcjonalną aplika-cję przygotowaną do wdrożenia na serwerze. Główne etapy wytwarzania aplikacji obejmą:

1. Konfigurację środowiska programistycznego wraz z niezbędnymi bibliotekami oraz uruchomienie tradycyjnego przykładu „Hello World”.

2. Charakterystyka JSF 2.0 – porównanie do wersji JSF 1.2, najciekawsze zmiany (w tym zintegro-wane Facelety, wbudowane wsparcie dla Ajax, zmieniony sposób ładowania zasobów, wsparcie dla GET)

Wiosna z drugą twarzą w chmurach – część I

Dowiesz się:• Jak zainstalować środowisko programistyczne wraz z serwe-

rem aplikacji;• Jak pobrać i zainstalować wymagane biblioteki;• Jak stworzyć prostą, kompletną i działającą aplikację JSF 2.0 w

nowym środowisku.

Powinieneś wiedzieć: • Znać podstawy języka Java oraz technologii Java EE;• Znać podstawy JSF 1.2;• Znać podstawy HTML-a.

Kompletny przykład procesu wytwarzania aplikacji Java Server Faces 2.0 z użyciem Spring Framework i Hibernate wraz z jej końcowym wdrożeniem na chmurę obliczeniową Amazon Elastic Compute Cloud (EC2).

Rysunek 1. Katalog instalacyjny Java JDK Rysunek 2. Katalog instalacyjny Springsource Tool Suite

Page 43: SDJ 08 2010

8/201042

PROGRAMOWANIE JAVA Wiosna z drugą twarzą w chmurach – część I

www.sdjournal.org 43

aktualną wersją jest wersja 2.0 z 2009 roku, chociaż dość popularna jest także wersja 1.2 z roku 2006.

Przykładowa aplikacjaCelem niniejszego artykułu jest pokazanie, jak szyb-ko stworzyć szkielet działającej aplikacji w oparciu o JSF 2.0. Czytelnik zapozna się ze wszystkimi kro-

3. Bezpieczeństwo aplikacji webowej – autoryzacja użytkownika oraz zasady bezpieczeństwa dostę-pu do zasobów w oparciu o Spring Security Fra-mework.

4. Integracja z bazą danych – persystencja przy użyciu narzędzi ORM, wykorzystanie Spring Framework.

5. Wdrożenie na lokalny serwer oraz wdrożenie na chmurę obliczeniową Ama-zon.com

Od kilku lat obserwujemy nie-ustanny wzrost popularności aplikacji webowych. Idea, jaka za tym stoi, jest prosta: klient nie musi mieć na kompute-rze niczego poza przeglądar-ką internetową. Serwisy inter-netowe wyewoluowały z pro-stych informacyjnych stron do bogatych interaktywnych apli-kacji uruchamianych w prze-glądarce. Powstałe i wciąż powstające technologie wido-ku oferują graficzne kompo-nenty i zachowanie jeszcze do niedawna zarezerwowa-ne aplikacjom typu desktop. Można powiedzieć, że apli-kacje webowe coraz bardziej stają się podobne do swoich odpowiedników typu desktop w kontekście możliwości.

Jedną z dostępnych techno-logii wytwarzania aplikacji we-bowych jest Java Server Fa-ces (JSF). Ma ona z założenia łączyć pisanie aplikacji serwe-rowych Javy z szybkim wytwa-rzaniem bogatego interfejsu użytkownika. Innymi słowami: jest czymś na wzór „Swinga dla aplikacji serwera”. Główne cechy JSF to:

• zbiór gotowych komponen-tów interfejsu użytkownika

• model programowania oparty o zdarzenia

• możliwość tworzenia wła-snych dodatkowych kom-ponentów

Technologia JSF jest dostęp-na od roku 2004, kiedy to po-wstała wersja 1.0. Obecnie

Rysunek 3. Wybór katalogu instalacji

Rysunek 4. Wybór katalogu z zainstalowaną Javą JDK

Page 44: SDJ 08 2010

8/201044

PROGRAMOWANIE JAVA Wiosna z drugą twarzą w chmurach – część I

www.sdjournal.org 45

kami, jakie są niezbędne, żeby wykonać i uruchomić przykładową aplikację na serwerze.

Jako środowisko programistyczne (IDE) wykorzy-stany zostanie Springsource Tool Suite (STS). Śro-dowisko to zawiera wbudowaną specjalną wersję Tomcata (nazywaną „tc Server”) dla aplikacji w Ja-vie, które wykorzystują Spring Framework. Ponieważ Spring nie będzie na razie wykorzystywany, wbudo-wany Tomcat będzie służył jako tradycyjny serwer, na którym będzie wdrażana i uruchamiana przykładowa aplikacja.

Instalacja środowiskaZanim przystąpimy do pracy ze Springsource Tool Suite, potrzebujemy zainstalować Java JDK. Dla za-

chowania porządku proponuję na dysku C utworzyć katalog „Development”, a w nim w katalogu Java za-instalować JDK. Całość powinna wyglądać tak (Ry-sunek 1).

Następnie przygotujmy katalogi dla Springsource Tool Suite w katalogu Development. Będą to dwa ka-talogi: instalacyjny dla samego IDE o nazwie „Spring-ToolSuite” oraz katalog roboczy (workspace) dla te-go środowiska o nazwie „SpringToolSuite_Workspa-ce”. Wygląda to tak (Rysunek 2).

Czas na pobranie Springsource Tool Suite. Uda-jemy się do strony http://www.springsource.com/products/springsource-tool-suite-download i tam po rejestracji otrzymujemy możliwość ściągnięcia środo-wiska. Wybieramy instalator dla Windows w wersji 32

bitowej (w momencie pisania te-go artykułu plik instalatora miał nazwę springsource-tool-suite-2.3.2.RELEASE-e3.5.2-win32-in-staller.exe). Zapisujemy nasz plik instalatora i uruchamiamy go. Instalator składa się z 8 kro-ków (pokażę te najbardziej istot-ne). Krok 1 i 2 to ekran powitalny i wyświetlenie licencji. Krok 3 jest ekranem wyboru katalogu do-celowego instalacji środowiska. Wybieramy utworzony uprzednio katalog „SpringToolSuite” z C:\Development: (Rysunek 3).

W kroku 4 instalator zapyta nas o komponenty, które chcemy za-instalować. Domyślnie wszystkie są zaznaczone i tak je pozosta-wiamy. W kroku 5 instalator pyta nas o lokalizację instalacji JDK. Zwróćmy uwagę, że instalator wy-świetla informację, mówiącą że potrzebna jest JDK, a nie JRE. Wskazujemy Javę z katalogu C:\Development\Java: (Rysunek 4).

Krok 6 to instalacja środowiska wraz z wyświetlanym postępem prac: (Rysunek 5).

Krok 7 to tworzenie skrótów w Menu Start, natomiast w kroku 8 możemy zdecydować, czy chce-my uruchomić środowisko po in-stalacji. Uruchamiamy je i na po-czątku jesteśmy pytani o wybranie domyślnego katalogu roboczego środowiska. Wskazujemy uprzed-nio utworzony katalog „SpringTo-olSuite_Workspace” w lokalizacji C:\Development: (Rysunek 6).

Rysunek 5. Postęp instalacji

Rysunek 6. Kon�guracja katalogu roboczego..

Page 45: SDJ 08 2010

8/201044

PROGRAMOWANIE JAVA Wiosna z drugą twarzą w chmurach – część I

www.sdjournal.org 45

Środowisko jest zainstalowane i gotowe do pra-cy. Zwróćmy uwagę, że w zakładce „Servers” na do-le pod nazwą „SpringSource tc Server v6.0” widocz-ny jest Tomcat. Możemy go uruchomić z poziomu środowiska i sprawdzić, czy faktycznie działa, wpi-sując po uruchomieniu w przeglądarce adres http://localhost:8080/. Podczas pierwszego uruchomienia serwera środowisko zapyta nas, czy chcemy urucho-mić aplikację o nazwie „Insight”, która monitoruje ser-wer oraz aplikacje na nim działające. Uruchomienie tej aplikacji nie jest obowiązkowe.

Hello World w JSF 2.0Mamy zainstalowane i działające środowisko, moż-na przystąpić do tworzenia najprostszej przykładowej

aplikacji JSF 2.0. Powstanie przykładu będzie moż-liwie najprostszym procesem, aby nie komplikować sprawy, nie używam tutaj Mavena, który jest zinte-growany ze środowiskiem. Cały proces przedstawię w kolejnych krokach.

Krok 1. Projekt w IDETworzymy nowy dynamiczny projekt webowy, który będzie uruchamiany za pomocą Tomcata. Jest on do-stępny w sekcji "web" menu File->New->Project: (Ry-sunek 7).

Na kolejnych ekranach przechodzimy dalej, nicze-go nie zmieniając, aż do wybrania opcji „Finish”. Na końcu powinniśmy otrzymać strukturę podobną do: (Rysunek 8).

Rysunek 7. Nowy projekt aplikacji webowej

Page 46: SDJ 08 2010

8/201046

PROGRAMOWANIE JAVA Wiosna z drugą twarzą w chmurach – część I

www.sdjournal.org 47

Krok 2. Instalacja niezbędnych bibliotekBędziemy potrzebować bibliotek JSF 2.0 (używamy implementacji MyFaces) oraz JSTL. Biblioteki MyFa-ces są dostępne na stronie http://myfaces.apache.org/download.html (wersja MyFaces Core 2.0.0 (zip)) ja-ko pojedynczy plik .zip, natomiast JSTL znajduje się na stronie https://jstl.dev.java.net/ (pobieramy 2 pliki .jar: jstl-api-1.2.jar oraz jstl-impl-1.2.jar).

Listing 1. Deskryptor wdrożenia web.xml dla przykładowej aplikacji

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://

java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"

version="2.5">

<display-name>JSFSample</display-name>

<servlet>

<servlet-name>Faces Servlet</servlet-name>

<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>Faces Servlet</servlet-name>

<url-pattern>/faces/*</url-pattern>

</servlet-mapping>

<welcome-file-list>

<welcome-file>faces/index.xhtml</welcome-file>

</welcome-file-list>

</web-app>

Listing 2. Plik WelcomeUserBean.java

/**

*

*/

package com.jsfsample;

import javax.faces.bean.ManagedBean;

import javax.faces.bean.RequestScoped;

/**

* @author Pawel

*

*/

@ManagedBean(name="welcomeBean")

@RequestScoped

public class WelcomeUserBean {

public String sayHello(){

return "message";

}

}

Listing 3. Plik HelloMessageBean.java

/**

*

*/

package com.jsfsample;

import javax.faces.bean.ManagedBean;

import javax.faces.bean.RequestScoped;

/**

* @author Pawel

*

*/

@ManagedBean(name="messageBean")

@RequestScoped

public class HelloMessageBean {

private String name;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String goBack(){

return "welcome";

}

}

Page 47: SDJ 08 2010

8/201046

PROGRAMOWANIE JAVA Wiosna z drugą twarzą w chmurach – część I

www.sdjournal.org 47

Implementacja MyFaces ma tę przewagę nad im-plementacją referencyjną, że jest pozbawiona kil-ku uciążliwych bugów oraz posiada szereg ulepszeń i usprawnień.

Pliki jar z obydwu archiwów kopiujemy fizycznie do katalogu WEB-INF\lib i odświeżamy widok projektu w środowisku (naciskając F5 na całym projekcie). Bi-blioteki automatycznie pojawią się w sekcji „Web App Libraries”. Jest to najprostsza metoda dodania bi-

bliotek. Alternatywną metodą jest stworzenie biblio-tek użytkownika na podstawie powyższych plików .jar i dodanie ich do projektu. W przypadku korzystania z Mavena zarządzanie zależnościami bibliotek spa-dłoby na niego.

Rysunek 8. Struktura projektu Rysunek 9. Struktura kompletnego projektu

Listing 4. Strona welcome.xhtml

<?xml version='1.0' encoding='UTF-8' ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-

transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"

xmlns:ui="http://java.sun.com/jsf/facelets"

xmlns:h="http://java.sun.com/jsf/html">

<h:head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<title>JSFSample</title>

</h:head>

<h:body>

<h:form>

Hello, what is Your name?<br />

<h:inputText value="#{messageBean.name}" /><h:commandButton value="Next" action="#{welcomeBean.sayHello}"

/>

</h:form>

</h:body>

</html>

Page 48: SDJ 08 2010

8/201048

PROGRAMOWANIE JAVA Wiosna z drugą twarzą w chmurach – część I

www.sdjournal.org 49

Listing 5. Strona message.xhtml

<?xml version='1.0' encoding='UTF-8' ?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-

transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"

xmlns:ui="http://java.sun.com/jsf/facelets"

xmlns:h="http://java.sun.com/jsf/html">

<h:head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<title>Welcome :)</title>

</h:head>

<h:body>

<h:form>

Hello, nice to meet You <h:outputText value=" #{messageBean.name}"/>.

<br />

<h:commandButton value="Back" action="#{messageBean.goBack}" />

</h:form>

</h:body>

</html>

Kącik ekspertaStając przed wyborem stosu technologii dla nowo-powstającego projektu webowego, stajemy przed jedną z najtrudniejszych decyzji architektonicznych. W świecie Javy wybór przestaje być przywilejem z uwagi na mnogość podejść i rozwiązań. W przy-padku wyboru stosu frameworków obowiązuje taka sama zasada jak w przypadku każdego nietrywialnego problemu – wszyst-ko zależy od kontekstu.W niniejszej serii artykułów prezentujemy sprawdzony zestaw technologii, który ma zastosowanie w następującym kontekście:

• Aplikacja webowa wspiera procesy biznesowy – dlatego zakładamy, że większość ekranów będą stanowić formularze. W tym wypadku JSF stanowi bardzo produktywne narzędzie do tworzenia tego typu widoków. Dodatkowo wersja 2 jest pozbawio-na większość ograniczeń wersji 1.x oraz posiada natywne wsparcie dla AJAX.

• Spring Framework integruje dwa frameworki webowe: Spring Web MVC oraz Spring Web Flow, które abstrahują od warstwy prezentacji. Dlatego zawsze mamy możliwość dodania (lub całkowitej wymiany) technologii prezentacji w razie potrzeb (np frameworka lżejszego niż JSF).

• Spodziewamy się dużej ilości operacji CRUD (Create Read Update Delete) – dlatego stosujemy maper relacyjno-obiektowy (ORM), który drastycznie zwiększa produktywność w aplikacjach tej klasy. Oczywiście zawsze możemy posłużyć się natyw-nym SQL w razie potrzeby – mamy wsparcie ze strony Spring Framework. W kolejnych częściach zaprezentujemy architektu-rę Command-query Responsibility Segregation, gdzie wykorzystany będą oba podejścia: ORM i natywny SQL.

• Spring Framework to sprawdzony zestaw bibliotek, który w odróżnieniu od innych tego typu rozwiązań charakteryzuje się bardzo wysoką jakością (zarówno na poziomie kodu, jak i na poziomie koncepcyjnym), co w krytycznych projektach ma nie-bagatelne znaczenie. Spring możemy wykorzystać do wielu celów, natomiast w tej serii zaprezentujemy jego główną funk-cjonalność – wsparcie dla paradygmatu Inversion of Control. Spring wspiera trzy techniki IoC: Wstrzykiwanie zależności, Zda-rzenia i Aspect Oriented Programming. W kolejnych częściach zaprezentujemy praktyczne wykorzystanie technik IoC oraz ich wpływ na powstanie systemu o architekturze otwartej na rozbudowę oraz podatnej na testy.

• Środowisko chmurowe jest alternatywą dla klasycznego skalowania znanego np. ze środowisk Java EE. Chmury zdobywają również popularność w biznesie ze względu na modę na wirtualne oprogramowanie. W niniejszej serii będzie opierać się na chmurze EC2, która posiada wsparcie ze strony Spring Tools Suite. EC2 oferuje jedynie zasoby sprzętowe – w odróżnie-niu np od chmury Google, która oferuje również platformę developerską. Aktualnie jednak platformy developerskie po-siadają szereg ograniczeń. Niedawno doszło jednak do porozumienia Google ze SpringSource, co mam nadzieje zaowo-cuje lepszym wsparciem chmury Google dla pełni wykorzystania Spring Framework (które na dzień dzisiejszy pozostawia wiele do życzenia).

Sławomir SobótkaTrener i konsultant w firmie Bottega – Akademia [email protected]://art-of-software.blogspot.com

Page 49: SDJ 08 2010

8/201048

PROGRAMOWANIE JAVA Wiosna z drugą twarzą w chmurach – część I

www.sdjournal.org 49

Krok 3. Modyfikacje w pliku web.xmlWygenerowany plik web.xml nie zawiera niczego po-za listą plików domyślnie uruchamianych po wpisaniu adresu aplikacji w przeglądarce. Musimy zmodyfiko-wać ten plik na potrzeby JSF. Nowa zawartość tego pliku przedstawia się następująco: (Listing 1).

Krok 4. Tworzenie klas komponentów (beans)Najpierw w katalogu „src” tworzymy pakiet „com.jsfsample”, a w nim umieszczamy dwie klasy : WelcomeUserBean.java oraz HelloMessageBean.java widoczne na kolejnych listingach (Listing 2, 3).

Za pomocą adnotacji @ManagedBean zdefiniowaliśmy rolę każdej z klas wraz z nazwą, która pozwoli na od-wołanie się do obiektu danej klasy z poziomu stro-ny .xhtml. Adnotacja @RequestScoped określa czas ży-cia (zasięg) danego beana – w tym wypadku będzie to request.

W klasach znajdują się dwie metody sayHello() oraz goBack(), które będą odpowiedzialne za nawi-gację pomiędzy stronami, które za chwilę stworzy-my.

Krok 5. Proste strony xhtmlMamy klasy beanów, w których w bardzo prosty spo-sób przechowujemy dane oraz używamy ich do pro-stej nawigacji. Teraz stworzymy pliki stron, które bę-dą korzystać z beanów poprzez zapisywanie i wy-świetlanie zawartych w nich danych. Metody na-wigacyjne zawarte w beanach będą wykorzysta-ne do przechodzenia między stronami. Dla beana WelcomeUserBean.java stworzymy stronę o nazwie „welcome.xhtml” widoczną na listingu poniżej: (Li-sting 4).

Dla beana HelloMessageBean.java strona .xhtml bę-dzie miała postać: (Listing 5).

Zwróćmy uwagę na sposób odwoływania się ze stron .xhtml do instancji danego beana i jego składo-wych (zmiennych, metod). Używając języka wyrażeń (expression language, EL) i w połączeniu z konwen-cją JavaBeans, możemy uzyskać dostęp do konkret-nego beana i jego składników. Za przykład niech po-służy wyświetlenie wpisanego uprzednio imienia na stronie message.xhtml za pomocą znacznika:

<h:outputText value=" #{messageBean.name}"/>

Wyrażenie „messageBean” to nic inne-go jak umowna nazwa naszego beana (klasy) HelloMessageBean.java podana w adnotacji @Mana

gedBean(name="messageBean"), natomiast „name” to zmienna instancyjna (pole) w tej klasie. Zgod-nie z konwencją JavaBeans próba pobrania war-tości z tego pola zaowocuje wywołaniem metody getName() z beana HelloMessageBean.java

UruchomieniePrzykładowa aplikacja jest gotowa. Struktura projek-tu wraz ze wszystkimi stworzonymi i dodanymi plika-mi powinna wyglądać tak: (Rysunek 9).

Możemy wdrożyć ją na serwer poprzez kliknięcie prawym przyciskiem myszy na całym projekcie i wy-branie z menu kontekstowego opcji Run As –> Run on Server. Pojawi się okno, w którym IDE zapyta, na jakim serwerze chcemy uruchomić aplikację. Ponie-waż nie posiadamy zdefiniowanych innych serwe-rów poza „SpringSource tc Server v6.0”, wybieramy ten i klikamy „Next”. Kolejne okno pokazuje wszyst-kie dostępne aplikacje z możliwością uruchomienia ich na wybranym serwerze. Tutaj niczego nie zmie-niamy i klikamy „Finish”. Ta operacja spowoduje uru-chomienie serwera i wdrożenie naszej przykładowej aplikacji.

Po chwili wystarczy wpisać w przeglądarkę ad-res http://localhost:8080/JSFSample/, aby zobaczyć działający przykład.

PodsumowaniePowyższy przykład pokazuje, jak w szybki sposób stworzyć najprostszą możliwą aplikację JSF 2.0 i uru-chomić ją na zintegrowanym w tym wypadku serwe-rze Tomcat. Główny nacisk położyłem tutaj na szyb-kie wręcz „wyklikanie” działającego projektu z mini-malną konfiguracją. Dla uproszczenia omawianego przykładu nie używam tutaj żadnych nowości, które oferuje nam JSF 2.0 w porównaniu do JSF 1.2 (je-dyna użyta rzecz to brak pliku faces-config.xml, czyli konfiguracja za pomocą adnotacji i prosta nawigacja bezpośrednio w beanach). Także celowo pominąłem użycie Mavena do zarządzania zależnościami i struk-turą projektu, aby nie opisywać dodatkowo specyficz-nych dla Mavena kroków konfiguracyjnych w tak pro-stym przykładzie.

Przykład ten jako działająca aplikacja może z po-wodzeniem służyć do dalszych własnych ekspery-mentów z technologią JSF 2.0.

PAWEŁ NIEŚCIORUKAutor jest programistą Java. W pracy zawodowej bierze udział w analizie, projektowaniu oraz wytwarzaniu webo-wych aplikacji w środowisku Java EE z użyciem najnowszych frameworków. Współpracuje także z �rmą Bottega. Prywat-nie interesuje się całościowym podejściem do wytwarzania aplikacji webowych oraz bazami danych i ich optymaliza-cją. Prowadzi autorskiego bloga poświęconego Javie i pro-gramowaniu, który jest rodzajem notatnika ze wskazówka-mi dla początkujących programistów. Kontakt z autorem: [email protected], blog: http://technology-for-human.blogspot.com

Page 50: SDJ 08 2010

8/201050

NIEZAWODNE OPROGRAMOWANIE Testowanie gier na urządzenia mobilne

www.sdjournal.org 51

Testowanie oprogramowania, a więc także i gier to, jak można przeczytać w jednej z wielu róż-nych definicji: używanie aplikacji w kontrolowany

sposób i ocena rezultatów tego działania. Pojęcie kon-trolowany sposób oznacza oczywiście zarówno warun-ki typowe, normalne, jak i warunki nietypowe. W cza-sie testowania należy świadomie starać się działać źle, aby stwierdzić, czy coś się dzieje, gdy nie powinno, al-bo nie dzieje, gdy powinno – działanie jest zorientowa-ne na wykrywanie. Zapewnienie jakości to pojęcie szer-sze, obejmujące cały proces tworzenia oprogramowa-nia. To zapewnienie, że wszystkie wymagane standar-dy i procedury są spełnione, oraz że znalezione proble-my są odpowiednio naprawione – działanie nastawione bardziej na prewencję.

Dlaczego testowanie jest potrzebne?Co jest misją testera, jakie są jego główne zadania? Pierwsze i najistotniejsze to oczywiście znajdowa-nie błędów, zwłaszcza tych najważniejszych. Ponad-to tester powinien dokonywać ogólnej oceny testo-wanego produktu oraz sprawdzać, czy jest on zgod-ny z koniecznymi do spełnienia wymaganiami i stan-dardami. Zbiór subiektywnych ocen różnych teste-rów pozwala na polepszenie ogólnej jakości produk-tu. Dodatkowymi zadaniami testerów są ulepszanie i usprawnianie procesu testowania, aby minimalizo-wać potrzebny czas, koszt i efekty uboczne, czy po-

stępowanie zgodnie z wyznaczonymi zadaniami i za-kresem pracy.

Dla kogo tak naprawdę pracuje zespół testerów? Wy-daje się, że głównie dla producenta gry – on odpowia-da za końcowy kształt projektu i chce mieć pewność, że proces testowania przebiega sprawnie w każdej fazie, jest dokładny, skuteczny i gwarantuje dostarczenie pro-duktu wysokiej jakości w wymaganym czasie. Można także stwierdzić, że dla programistów – przygotowując precyzyjne raporty i opisy błędów, umożliwia szybsze, łatwiejsze i mniej czasochłonne ich naprawienie. Do-datkowo można tu wymienić także inne aspekty pracy testerów, uzasadniające, że ich praca służy pośrednio także działowi marketingu czy zarządowi. Ale tak na-prawdę, zwłaszcza w przypadku branży gier, wokół któ-rej się obracamy, zespół testerów służy graczom. Naj-większą korzyścią z ich pracy nad produktem jest wła-śnie satysfakcja odbiorcy i jego zadowolenie z jakości finalnego produktu.

Skąd się biorą błędy i kiedy testować?Skąd tak naprawdę biorą się błędy w grach? Najbar-dziej do ich powstawania przyczynia się złożoność oprogramowania. Złożoność współczesnych aplika-cji jest trudna do pojęcia dla osoby bez doświadczenia w branży. Niektóre błędy pojawiają się na skutek bra-ku lub słabej komunikacji, co prowadzi do niepełnego zrozumienia wymagań i nieprawidłowego ich spełnie-

Testowanie gier na urządzenia mobilneTestowanie gier jest niezwykle złożoną materią, zwłaszcza przy dużych, wysokobudżetowych produkcjach. Gry na urządzenia mobilne zwykle do takich nie należą, co nie oznacza, że proces testowania jest tu mało istotny czy możliwy do pominięcia. Jaka jest specyfika tego procesu, dowiecie się z niniejszego artykułu.

Dowiesz się:• Skąd się biorą błędy;• Kiedy i jak testować grę;• Jakie są specyficzne aspekty testowania gier mobilnych;• Jakie ograniczenia napotkasz przy testowaniu gier na niektóre

konsole.

Powinieneś wiedzieć:• Co to jest gra komputerowa.• Co to jest urządzanie mobilne.

Page 51: SDJ 08 2010

8/201050

NIEZAWODNE OPROGRAMOWANIE Testowanie gier na urządzenia mobilne

www.sdjournal.org 51

znajdowaniem błędów najlepszy czas przychodzi po tym, jak gra osiągnie fazę alpha, czyli zawiera znacz-ną część końcowej funkcjonalności. Obszar i intensyw-ność tych testów należy oczywiście zintensyfikować po osiągnięciu fazy beta, gdzie funkcjonalność jest już zwykle w pełni ukończona i przychodzi czas na napra-wianie błędów, które do gry się zakradły.

W przypadku gier jest jeszcze jedna istotna rzecz, która grę odróżnia od oprogramowania użytkowego czy korporacyjnego – tego typu oprogramowanie uwa-ża się za dobre, jeśli wypełnia bezbłędnie funkcje, do jakich zostało stworzone. Do stworzenia dobrej gry to nie wystarczy, gra musi być przede wszystkim grywalna i atrakcyjna (wizualnie, dźwiękowo, funkcjonalnie). Dla-tego istotne jest subiektywne zdanie zespołu testerów (chociaż nie tylko) odnośnie tego, jak gra wygląda, czy jest intuicyjna, funkcjonalna, czy poziom trudności i je-go progresja są odpowiednio dobrane, czy wreszcie gra ma to coś, co powoduje, że gracz nie może się od niej oderwać i ciągle chce poświęcić jej jeszcze chwilkę.

Jak testować?Jeśli wiadomo już mniej więcej, po co i od kiedy testo-wać, należy odpowiedzieć sobie na pytanie – jak te-stować? Jakie metody testowania można wyróżnić, ja-kich narzędzi użyć, czy można proces w pełni zauto-matyzować?

W zasadzie prawie wszystkie rodzaje testów (a tych rodzajów i klasyfikacji jest bardzo dużo) moż-na podzielić na testy grupy black box (czarnej skrzynki, czyli po polsku testy funkcjonalne) oraz white box (białej skrzynki, czyli testy strukturalne). Zasadniczą różnicą między nimi jest poziom, na którym działają testerzy.

W przypadku testów funkcjonalnych testerów intere-suje zachowanie się aplikacji przy zadanych warunkach wejściowych, a więc najprościej mówiąc, sprawdzenie działania aplikacji od strony funkcjonalnej, czyli zgod-ności jej działania z założeniami i wymaganiami. Te-go typu testowanie nie wymaga znajomości kodu apli-kacji, a jedynie obeznania z charakterystyką jej działa-nia i stawianymi przed nią wymaganiami. Do tego ty-pu testów można zaliczyć m.in. acceptance testing (te-sty akceptacyjne – mające na celu ostateczną akcepta-cję produktu i potwierdzenie jego zgodności z założe-niami), usability testing (testy użytkowe – które mają za zadanie sprawdzić np. przyjazność interfejsu dla użyt-kownika), install/uninstall testing (testy instalacji i dein-stalacji, sprawdzające prawidłowość działania tych ope-racji), compatibility testing (testy kompatybilności z róż-nymi wersjami sprzętu lub systemu), exploratory testing (testy eksploracyjne – swobodne testowanie na zasa-dzie podążania za wiedzą z dokumentacji, bez plano-wania czy trzymania się planów testów) czy ad–hoc te-sting (testy doraźne – testowanie całkowicie bez planu, jak i dokumentacji).

nia. Część błędów wynika po prostu z błędów progra-mistycznych, które programiści, jak wszyscy inni ludzie, mogą popełniać.

Jedną z ważniejszych przyczyn błędów, zwłaszcza w końcowych fazach projektu, jest zmiana wymagań – osoby stojące na zewnątrz projektu mogą nie zdawać sobie sprawy lub nie rozumieć, jakie skutki może przy-nieść żądanie określonych zmian. A może to wymagać przeprojektowania, przydzielenia innych osób realizują-cych, dodatkowej optymalizacji, i wtedy dokonywanie zarówno małych, jak i większych zmian może wpłynąć na wiele przewidywalnych i nieprzewidywalnych zależ-ności, uprawdopodabniając powstanie w którymś miej-scu błędu.

Z dokonywaniem zmian, zwłaszcza w końcowej fazie projektu, związana jest presja czasu – to zjawisko na pewno nie pomaga pozbywaniu się błędów, lecz wręcz przeciwnie – sprzyja ich powstawaniu.

I wreszcie całkowicie zewnętrzna przyczyna błę-dów, ale przy tworzeniu gier często niestety występują-ca – narzędzia wspomagające. Nie da się tworzyć gier bez dodatkowych narzędzi, edytorów, bibliotek czy mid-dleware (oprogramowania pośredniczącego). Same te wszystkie dodatki zawierają w sobie błędy (które akurat przy użyciu ich do naszej aplikacji mogą się ujawnić) al-bo ich nie do końca poprawne użycie może takie błędy wprowadzić.

Skoro wiadomo już, jakie są główne przyczyny błę-dów, to pada pytanie – kiedy testować? Nie ma na to jed-noznacznej odpowiedzi. Wszystko zależy od tego, w ja-ki sposób prowadzony jest projekt, czy wszystkie jego istotne elementy realizowane są równolegle i składane w działającą całość w dalszej fazie, czy projekt rozwija się od małego prototypu, przez obudowywanie go dodat-kowymi elementami. W procesie tworzenia gier bardzo często można wyróżnić fazy, których zakończenia (mi-lestones) określane są nazwami first playable (grywal-ny prototyp, często jedynie podstawowa funkcjonalność i tymczasowy wygląd), wersja alpha (dodana znaczna część funkcjonalności i w dużej mierze finalny wygląd), wersja beta (pełna funkcjonalność i ostateczny wygląd, może zawierać jeszcze błędy), wersja release candida-te (czyli kandydat do wypuszczenia na zewnątrz – prak-tycznie ukończona gra, bez braków i istotnych błędów, może zawierać drobne błędy, które są mało istotne al-bo nie zostały znalezione) i ostatecznie wersja gold ma-ster (po ostatecznych testach akceptacyjnych, gotowa do wydania, nie zawiera znanych i znalezionych błędów czy braków w funkcjonalności).

Podany schemat jest oczywiście przykładowy i po-szczególne projekty mogą stosować się do jego od-mian. W zależności od tego, co w konkretnym projek-cie zawiera każda z faz, można wybrać najlepszy czas na rozpoczęcie testów i określić ich zakres oraz inten-sywność. Zwykle na pierwsze testy związane stricte ze

Page 52: SDJ 08 2010

8/201052

NIEZAWODNE OPROGRAMOWANIE Testowanie gier na urządzenia mobilne

www.sdjournal.org 53

Testy strukturalne patrzą na testy z punktu widzenia wewnętrznej struktury projektu i zajmują się sprawdza-niem poprawności działania wszelkich możliwych ście-żek w strukturze programu. Oczywiście wymaga to od-powiednio wysokich umiejętności programistycznych i dobrej znajomości kodu aplikacji. W przypadku ko-nieczności prowadzenia takich testów, zajmują się tym wyznaczeni programiści lub przygotowani do tego inży-nierowie testów.

W branży gier, w odróżnieniu od branży oprogramo-wania korporacyjnego, przeważa zdecydowanie testo-wanie funkcjonalne, które skupia się na testowaniu pro-duktu w taki sposób, jak końcowy odbiorca będzie go używał. Specjalną odmianą testów, popularną, często spotykaną i przynoszącą wiele korzyści, jest playte-sting. Jego głównym celem nie jest znalezienie błędów, a sprawdzenie, jak różni odbiorcy postrzegają grę, jak ją oceniają, czy są w niej elementy frustrujące, co uwa-żają za konieczne do zmiany. Istotnymi elementami są dobór osób, które stanowią grupę docelową (zdecydo-wanie nie powinny to być osoby związane z projektem i znające go, a ponadto powinna to być grupa zawie-rająca osoby pasujące do typu odbiorcy docelowego, do którego gra jest kierowana), oraz sposób przepro-wadzenia testu i określenia jego wyników (tu pomoc-ne jest współuczestniczenie z testującym w sesji testo-wej i notowanie na bieżąco jego spostrzeżeń, reakcji, komentarzy, ewentualnie udostępnienie mu szczegóło-wego formularza z pytaniami tuż po zakończeniu testu). Playtesting może być przeprowadzony także w trybie zewnętrznym, na grupie osób, które zadeklarują chęć przystąpienia do niego i udzielenia odpowiedzi na za-dane pytania. Istotna jest faza projektu, w której tego ty-pu testy są prowadzone. Ponieważ rezultaty playtestin-gu pozwalają na poznanie opinii grupy reprezentującej końcowych odbiorców, ich opinie powinny być uwzględ-nione w projekcie na tyle, na ile to możliwe. Dlatego te-sty takie powinny być przeprowadzone odpowiednio wcześnie, aby wystarczyło czasu na konieczne zmiany (które niekiedy mogą być nawet fundamentalne), ale na tyle późno, aby grupa testująca wypowiadała się i oce-niała grę w postaci zbliżonej do ostatecznej (mechanika gry, jej logika, elementy graficzne i dźwiękowe powinny być w ostatecznej formie). Dobrze jest przeprowadzić kolejną rundę playtestingu po wprowadzeniu zmian na podstawie zgłaszanych uwag, aby sprawdzić, czy cho-ciaż w pewnym stopniu wprowadzone zmiany przynio-sły zakładany skutek.

Jak wygląda kwestia automatyzacji testowania gier na platformy mobilne? Nie jest to takie proste. Automaty-zacja jest stosowana dosyć powszechnie w przypadku chociażby aplikacji enterprise. Niestety specyfika gier jest taka, że bardzo duże znaczenie ma aspekt wizual-ny, odczucia towarzyszące grze czy jej ścisłe dopaso-wanie do konkretnego urządzenia (zarówno pod kątem

graficznym, wydajnościowym, jak i zgodności z funkcjo-nalnością urządzenia). Z tego wynika przewaga (i zwy-kle konieczność) manualnego testowania funkcjonalne-go takich gier. Jednakże nie do końca jest tak, że nic się w zakresie automatyzacji nie dzieje. Warto przywo-łać tu dwa przykłady. Jeden z nich to system TestQuest. Pozwala on dosyć skutecznie testować aplikacje m.in. na zasadzie wywoływania odpowiednich akcji, które są określone przez skrypt testujący, a następnie porówny-wanie ekranów wyświetlanych przez urządzenie (czy-li telefon) z ekranami wzorcowymi. Każde odstępstwo od wzorca może zostać zaznaczone jako błąd. Ogra-niczeniami tutaj są po pierwsze konieczność posiada-nia urządzeń, na których gra ma być testowana, a tak-że istnienie tego rozwiązania jedynie dla aplikacji mo-bilnych działających w systemie operacyjnym Symbian. Inne ciekawe rozwiązanie to usługa Device Anywhe-re firmy Mobile Complete. Pozwala ona na zdalny do-stęp do ponad tysiąca rzeczywistych, fizycznych telefo-nów pracujących w kilkudziesięciu sieciach operatorów. Już sam zdalny dostęp daje duże korzyści – nie ma ko-nieczności posiadania wszystkich urządzeń, na któ-rych ma być przetestowana gra. Ponadto dzięki funk-cji Test Automation można tworzyć i uruchamiać scena-riusze testowe podobne do opisanych powyżej, działa-jące na dowolnym telefonie dostępnym w bazie. Z roz-wiązania tego można skorzystać, kiedy nie ma się do-stępu do rzeczywistego urządzenia, na którym można przeprowadzić testy.

A jak postąpić, kiedy nie ma się ani urządzenia, ani zasobów ludzkich do przeprowadzenia testów? Można skorzystać z pomocy jednej z wyspecjalizowanych firm testujących (test houses), zlecając przeprowadzenie te-stów według dostarczonego planu testów, albo testów precertyfikacyjnych, sprawdzających przygotowanie aplikacji do różnego rodzaju certyfikacji. Inne rozwiąza-nie to stosunkowo nowa usługa – mob4hire. Jest to ser-wis pośredniczący pomiędzy firmami i osobami fizycz-nymi, które posiadają telefony i mają chęć coś prze-testować za określone przez siebie wynagrodzenie, a twórcami oprogramowania chcącymi takie testowanie według ustalonego przez siebie planu zlecić.

Kto może testować?Na pozornie łatwe pytanie kto może zostać testerem gier? nie ma jednoznacznej odpowiedzi. Można oczy-wiście odpowiedzieć bez zastanowienia, że każdy (no może prawie każdy) – i będzie to poniekąd praw-da. Ale na pewno nie każdy może być dobrym teste-rem. Z pewnością zarówno namiętny gracz, spędzający przed ekranem i ulubionymi grami co najmniej kilka go-dzin dziennie, jak i osoba, która z zasady nie gra lub gra sporadycznie, mogą w jakimś stopniu przyczynić się do tego, że finalny produkt będzie lepiej wykonany, przy-niesie większą radość i mniej momentów frustracji koń-

Page 53: SDJ 08 2010

8/201052

NIEZAWODNE OPROGRAMOWANIE Testowanie gier na urządzenia mobilne

www.sdjournal.org 53

cowemu odbiorcy. Oczywiście wszystko zależy od te-go, czego oczekujemy od każdej z tych osób. Na pew-no nie można wymagać od gracza z przypadku, aby za-jął się sprawdzeniem, czy gra jest możliwa do przejścia na najtrudniejszym poziomie, albo czy współczynniki postaci są prawidłowo dobrane. Takie zadania wykona zdecydowanie lepiej gracz nałogowy. Jeśli jednak na-sza gra jest typową małą, lekką i przyjemną produkcją, planujemy skierować ją do szerokiej rzeszy odbiorców, to właśnie opinia przypadkowych graczy, którzy lubią czasami zagrać dla przyjemności w wolnej chwili, może być dla nas równie ważna, jak nie ważniejsza, zwłasz-cza przy ocenie intuicyjności wykonania interfejsu użyt-kownika czy przejrzystości pomocy do gry.

Czy można określić, jakimi cechami powinien wy-różniać się dobry tester? Oczywiście tak, różne źródła wymieniają listy cech i umiejętności, których posiada-nie może wpłynąć pozytywnie na pracę testera. Bez-sprzecznie tester powinien charakteryzować się do-kładnością, dociekliwością i systematycznością. Oso-ba podchodząca do wszystkiego pobieżnie, niedo-kładna, chaotyczna, nie powinna zabierać się za tę profesję. Kolejne ważne cechy to umiejętność czyta-nia i pisania. Zabrzmiało to oczywiście odrobinę hu-morystycznie, lecz nie chodzi tu oczywiście o stricte te umiejętności. Tester musi potrafić czytać nie powierz-chownie, lecz bardzo dogłębnie, intensywnie, ze sku-pieniem i przede wszystkim z dbałością o szczegóły. Niejednokrotnie od zrozumienia dokumentacji, spe-cyficznych wymagań czy oczekiwań, może zależeć właściwe podjęcie decyzji. A co z pisaniem? Ta cecha jest równie ważna, jeśli nie ważniejsza. I nie chodzi tu o stosowanie idealnych zasad gramatyki i ortografii, ale najważniejszą kwestią jest zdolność do skuteczne-go porozumiewania się w formie pisemnej. Kiedy te-ster znajduje problem, musi umieć opisać jego obja-wy w taki sposób, aby adresat nie miał najmniejszych wątpliwości. I często nie ma tu znaczenia, czy tester zobowiązany jest do pisania raportów w swoim ojczy-stym języku – chodzi tu bardziej o staranność i precy-zję w wyrażaniu myśli.

Jakie jeszcze inne cechy można wyróżnić? Można powiedzieć, że tester powinien mieć umiejętność psu-cia (testowanej aplikacji), patrzenia z punktu widzenia odbiorcy, dążenia do uzyskania jakości. Po części mu-si być dyplomatą, aby utrzymywać dobrą i przyjazną współpracę z resztą zespołu (któremu przecież musi wytykać błędy), dobrze jest też, gdy rozumie cały pro-ces tworzenia aplikacji.

Można w tym miejscu wpisać jeszcze wiele cech, umiejętności i zaleceń co do postępowania, jak cho-ciażby dobra organizacja pracy, umiejętność zarządza-nia czasem i ustalania priorytetów, łatwość przyswaja-nia nowej wiedzy i oczywiście prawie nigdy nie wyczer-pie się tematu.

Specyfika testowania gier na platformy mobilne – JME/Symbian/BREW/iPhoneGry na platformy mobilne od lat stanowią przeważającą część rynku testowania gier. Spowodowane jest to tym, iż takich gier powstaje bardo dużo i zwykle są to projek-ty małe lub co najwyżej średnie, o stosunkowo krótkim czasie produkcji. Do niedawna przeważającą część gier mobilnych stanowiły produkcje napisane w Java Plat-form Micro Edition – JME (dawniej J2ME). Zdecydowa-nie największym wyzwaniem przy testowaniu gier mo-bilnych napisanych w JME jest konieczność ich prawi-dłowego działania na ogromnej liczbie urządzeń, cha-rakteryzujących się skrajnie różnymi możliwościami i parametrami technicznymi. Pozornie zadanie wyda-je się łatwe – z założenia aplikacja JME powinna być zgodna z wymaganiami zawartymi w Unified Testing Criteria for Java(TM) Technology–based Applications for Mobile Devices. Wymagania te zostały stworzone przez porozumienie Unified Testing Initiative, w skład którego weszli najwięksi producenci telefonów komór-kowych. W teorii to tylko kilkadziesiąt przypadków te-stowych, określających wymagania względem aplikacji, ale sytuacja nieco komplikuje się, jeśli jesteśmy zobo-wiązani wykonać testy na kilkuset urządzeniach. To jest właśnie największe wyzwanie w testowaniu aplikacji na urządzenia obsługujące JME.

Dostosowanie przygotowanej aplikacji do działania na wielu różnych urządzeniach mobilnych z platformą JME (a więc głównie na ogromnej ilości różnych mo-deli telefonów komórkowych) nazywa się portingiem. Jest to bardzo duże wyzwanie zarówno dla programi-stów, jak i dla zespołu testerów. Przede wszystkim naj-większą przeszkodą jest mnogość urządzeń. Listy mo-deli telefonów, które muszą być wspierane przez apli-kację, sięgają często liczb rzędu kilkuset (500–600 nie należy do rzadkości). Jeśli uwzględnimy, iż aplikacja działa w kilku językach, uzyskujemy całkiem pokaź-ną ilość pracy do wykonania. Oczywiście nie do koń-ca jest tak trudno – wiele modeli telefonów jest ze so-bą zgodnych (kompatybilnych) i wersja aplikacji przy-gotowana na jeden model będzie działała na innym modelu o takiej samej rozdzielczości ekranu i podob-nych parametrach sprzętowych. Pozwala to na ograni-czenie ilości pracy związanej z testowaniem i w koń-cowym wyniku uzyskanie mniejszej liczby unikalnych wersji aplikacji. Takie działanie to grupowanie. Nie-stety nie jest to działanie automatyczne i uniwersal-ne – wiele zależy od konkretnej aplikacji, jej rozmia-ru, stopnia zaawansowania czy wykorzystania specy-ficznych cech urządzenia. Oczywiście można wstęp-nie określić, które urządzenia są za sobą potencjalnie kompatybilne, ale zgodność tą należy potwierdzić po-przez wykonanie podstawowych testów zgodności da-nej aplikacji, gdyż jest ona w wysokim stopniu zależna od konkretnego przypadku.

Page 54: SDJ 08 2010

8/201054

NIEZAWODNE OPROGRAMOWANIE Testowanie gier na urządzenia mobilne

www.sdjournal.org

Co jest największą bolączką przy przygotowywaniu wersji aplikacji działającej na różnych urządzeniach? Jednym z ograniczeń jest rozmiar pliku jar akceptowa-ny przez urządzenie (tzw. jar size). W przypadku wie-lu nowych telefonów ograniczenie to nie ma znaczenia, gdyż nie limitują one samego rozmiaru pliku aplikacji, ale starsze urządzenia mają takie ograniczenie. Próba pobrania pliku o większym rozmiarze niż maksymalnie obsługiwany skończy się zwykle wyświetleniem komu-nikatu o błędzie. Jest to jeden z ważniejszych testów, który decyduje o możliwości pomyślnego pobrania i za-instalowania aplikacji. Niekiedy dystrybutor oprogramo-wania dodatkowo nakłada ograniczenie na wielkość pli-ku, co powoduje konieczność limitowania tej wielkości nawet w przypadku urządzeń nie posiadających takie-go limitu. Drugie niezmiernie istotne ograniczenie to ilość pamięci dostępna do wykorzystania przez aplika-cję, czyli tzw. heap size. To ograniczenie jest zdecydo-wanie decydujące, gdyż nawet telefony nie ogranicza-jące rozmiaru instalowanego pliku mają ograniczenie ilości pamięci dostępnej dla aplikacji. Przy testowaniu gier wymaga to dokładnego sprawdzenia, gdyż nawet uruchamiająca się aplikacja może w dalszych etapach działania (czyli np. w którymś z dalszych poziomów gry) potrzebować większej ilości pamięci niż udostępniana przez urządzenie.

Na jakie inne specyficzne aspekty testowania gier na platformy mobilne trzeba zwrócić uwagę? Mno-gość urządzeń i w wielu przypadkach brak standardów, których trzymaliby się ich producenci, powodują, że w przypadku poszczególnych urządzeń trzeba dokład-nie sprawdzać zgodność działania w grze przycisków dostępnych dla użytkownika ze specyfikacją aplikacji. Analogiczna sytuacja ma miejsce, jeśli chodzi o zacho-wanie urządzenia w czasie różnorodnych zdarzeń sys-temowych, takich jak przychodzące połączenie, przy-chodząca wiadomość tekstowa, informacja o niskim poziomie naładowania baterii czy też np. zamknięcie przez użytkownika klapki telefonu. Wszystkie tego typu zdarzenia mogą mieć bardzo różny wpływ na działanie aplikacji, jak np. niewłaściwe odrysowanie grafiki, brak wyciszenia dźwięku gry w czasie trwania rozmowy, nie przywrócenie dźwięku po zakończonej rozmowie albo wręcz zawieszenie się lub zamknięcie aplikacji. Dodat-kową przeszkodą jest fakt, iż wywołanie takiego zda-rzenia systemowego w czasie różnych działań gry mo-że spowodować różny skutek, więc nie można poprze-stać na sprawdzeniu jednorazowym. Trzeba także pa-miętać, że nie zawsze na każdym telefonie uda się pra-widłowo i zgodnie ze specyfikacją aplikacji naprawić wszystkie zauważone odstępstwa i problemy. Niestety niektóre urządzenia nie pozwalają na prawidłową im-plementację wszystkich wymagań z racji braku wspar-cia sprzętowego co do nich (jednym z takich przykła-dów może być reakcja aplikacji na zamknięcie klapki te-

lefonu – oczekiwalibyśmy wyciszenia w tym momencie dźwięku gry, lecz zdarzają się modele telefonów, które nie wysyłają do aplikacji informacji o fakcie zamknięcia klapki, co nie pozwala prawidłowo zaimplementować wymaganego zachowania). Pewną odmianą tej przypa-dłości jest odmienne zachowanie się tych samych tele-fonów, ale z różną wersją oprogramowania systemowe-go – odmienne zachowanie gry na innej wersji oprogra-mowania jest wykrywane wtedy często dopiero w dal-szych fazach, w czasie testów akceptacyjnych.

Gry mobilne to nie tylko JME, ale także Symbian. Te-stowanie gier działających w systemie Symbian jest bar-dzo podobne do testowania gier JME. Część funkcjonal-na oraz wszelkiego rodzaju testy zachowania się apli-kacji w zderzeniu ze zdarzeniami systemowymi są ana-logiczne. Różnicę stanowi tu zbiór wymagań – w tym przypadku jest to dokument Symbian Signed Test Cri-teria. Dokument ten zawiera obostrzenia głównie w kon-tekście zachowania się testowanej aplikacji w stosun-ku do systemu operacyjnego i innych aplikacji. Speł-nienie tych wymagań jest obowiązkowe (i sprawdzane przez wyznaczone, niezależne firmy testujące), aby apli-kacja mogła zostać podpisana cyfrowo i posiadać sta-tus Symbian Signed. Oczywiście mogą istnieć także in-ne dodatkowe wymagania do spełnienia poza wymaga-niami Symbian Signed – na przykład przy tworzeniu gier i aplikacji dla firmy Nokia trzeba zachować (i przetesto-wać) zgodność z dodatkowymi specjalnymi wymagania-mi, które mają zapewnić m.in. zbliżoną obsługę i zacho-wanie różnego rodzaju aplikacji w interface telefonu.

Pewną część rynku gier na urządzenia mobilne sta-nowią gry działające na stworzonej przez firmę Qual-comm platformie BREW, rozpowszechnionej zwłasz-cza w Stanach Zjednoczonych. Przy testowaniu na-leży pamiętać o specyficznych wymaganiach, któ-re w przeważającej części zawarte są w dokumencie True BREW Testing Criteria. Wymagania te są bardzo obszerne i szczegółowe, nie są zbytnio zbieżne z wy-maganiami dla aplikacji JME czy Symbian, więc sta-nowią swego rodzaju wyzwanie dla zespołu testujące-go. Co istotne, każda dostarczona wersja aplikacji jest wnikliwie testowana przez niezależną firmę akcepta-cyjną (NSTL – National Software Testing Labs), więc nie może być mowy o przymykaniu oczu na jakiekol-wiek drobne odstępstwa od wymagań (o ile oczywi-ście problem nie wynika stricte z samego urządzenia). Dodatkowo operatorzy telekomunikacyjni (gdyż to oni są praktycznie wyłącznym dystrybutorem aplikacji BREW) określają także swoje własne wymagania, któ-re aplikacja musi spełniać, aby zostać dopuszczona przez nich do sprzedaży. Także ich testerzy przepro-wadzają testy akceptacyjne pod kątem tych wymagań, a więc dobre i prawidłowe przetestowanie gry BREW nie jest sprawą łatwą. Podkreślić należy jeszcze, że także inne przeszkody stoją przed testerami. Każda

Page 55: SDJ 08 2010

8/201054

NIEZAWODNE OPROGRAMOWANIE Testowanie gier na urządzenia mobilne

www.sdjournal.org

wersja aplikacji (dostosowana do danego modelu te-lefonu) musi posiadać dołączoną dokumentację w po-staci dokumentu ASD (Application Specification Docu-ment), który zawiera bardzo szczegółowe informacje na temat aplikacji, łącznie z dokładnym diagramem działania aplikacji (menu flow), szczegółowym opi-sem działania poszczególnych klawiszy czy specyficz-nymi ograniczeniami urządzenia, które uniemożliwia-ją spełnienie niektórych wymagań (device limitations). Nie ułatwia testowania także fakt, że większość telefo-nów wyposażonych w platformę BREW działa w tech-nologii CDMA, co uniemożliwia wykonanie testów, do których konieczne jest połączenie z siecią telekomu-nikacyjną. W tym przypadku w grę wchodzą jedynie rozwiązania pozwalające na zdalny dostęp do telefo-nów albo zlecenie wykonania części testów w miejscu, gdzie odpowiedni dostęp jest zapewniony.

Przy opisie testowania gier na platformy mobilne nie sposób nie wspomnieć o rozwijającym się dynamicznie od wielu miesięcy rynku gier na urządzenia przenośne firmy Apple (czyli iPhone i iPod Touch). W tym przypad-ku testowanie ułatwia fakt, że nie musimy tu zachować zgodności z ogromną ilością różnorodnych urządzeń. Mamy do wyboru jedynie cztery urządzenia i ewentual-nie różne wersje oprogramowania (w celu sprawdzenia kompatybilności wstecznej). Otwarta pozostaje oczywi-

ście kwestia śledzenia przyszłych zmian oprogramowa-nia urządzenia i ewentualnego testowania poprawności działania z nimi stworzonej wcześniej aplikacji. Specyfi-ka testowania gier na tę platformę jest nieco inna. Oczy-wiście testowanie funkcjonalne, mające za zadanie po-twierdzić zgodność działania aplikacji z założeniami de-signera i sprawdzić chociażby jej stabilność, także tu ma zastosowanie. Apple nie wprowadziło restrykcyjnych list wymagań do spełnienia. Twórcy oprogramowania muszą stosować się do zaleceń Apple, związanych głównie z in-terfejsem użytkownika, co ma pozwolić poszczególnym aplikacjom zachowywać się w systemie w sposób prze-widywalny i łatwy do odgadnięcia przez użytkownika.

Oprócz testowania aplikacji pod kątem zaleceń Ap-ple ważniejsza jest inna kwestia, która nabiera specjal-nego znaczenia w kontekście dużego nasycenia rynku aplikacjami i ogromnej konkurencji z racji niskich barier wejścia na rynek. Gra, która ma odnieść sukces, oprócz tego, że musi być w odpowiedni sposób rozreklamowa-na i promowana, powinna idealnie trafić w gust odbior-cy. Co za tym idzie, należy położyć bardzo duży na-cisk na wydanie produktu bardzo dobrej jakości (czy-li bez błędów mogących spowodować frustrację gra-cza czy utrudniających mu rozgrywkę), w pewien spo-sób unikalnego, ale jednocześnie lekkiego i przyjazne-go w odbiorze. Z tego powodu nie wolno pomijać w tym

Reklama

Page 56: SDJ 08 2010

8/201056

NIEZAWODNE OPROGRAMOWANIE

przypadku bardzo dużej roli playtestingu, który chociaż w części pozwoli przewidzieć, jakie opinie może wywo-łać produkt na rynku i jakie aspekty wymieniane są jako największe wady konieczne do poprawienia.

Specyfika testowania gier na konsole do gier – Sony PSP/Nintendo Wii/Nintendo DSi W ostatniej części warto napisać kilka słów o specyfi-ce testowania gier tworzonych na niektóre z konsol do gier (chociaż nie wszystkie są mobilne) i o aspektach, na jakie przy takim testowaniu należy zwrócić uwagę. W przypadku konsol do gier dużym utrudnieniem przy testowaniu jest fakt konieczności użycia do testów funk-cjonalnych specjalistycznego sprzętu, niedostępnego publicznie, który stanowi zamknięte środowisko testo-we. Zarówno Nintendo w przypadku konsol Wii i DSi, jak też Sony przy konsoli PSP wymagają zakupu tako-wego sprzętu dla testerów, gdyż tylko na nim będą oni w stanie uruchamiać przygotowane przez zespół pro-gramistów aplikacje.

Testowanie gier na powyższe konsole wymaga tak-że stosowania się do wymagań określonych przez pro-ducentów. Jest to bardzo ściśle przestrzegane, aby za-pewnić użytkownikowi końcowemu produkt, który bę-dzie działał w sposób prawidłowy, przewidywalny i od-powiednio zachowujący się w środowisku systemowym. Określenie dokładnych wymogów i ścisłe ich przestrze-ganie na pewno wymusza powstawanie gier wyższej ja-kości (oczywiście z technicznego punktu widzenia, nie-koniecznie grywalności). Wraz z wymaganiami dostar-czane są niekiedy małe narzędzia testujące, które uła-twiają sprawdzenie niektórych elementów w sposób automatyczny. Kolejna rzecz, która związana jest z wy-maganiami, to dokumentacja. Przeprowadzenie testów zgodności z wymaganiami (czyli w przypadku Ninten-do – procedura znana jako Lotcheck, a dla Sony – TRC Technical Requirement Checklist) niesie za sobą ko-nieczność wypełnienia obszernej dokumentacji, tym obszerniejszej, im aplikacja wykorzystuje większą ilość dodatkowych możliwości (obsługa połączeń on–line, wykorzystanie rozszerzeń sprzętowych, gra wielooso-bowa itp). Sprawy nie ułatwia fakt, że wszystkie wyma-gania, odnoszące się do samej aplikacji, jak i do sposo-bu jej przygotowania do dystrybucji, zawarte są często w wielu różnych dokumentach posiadających wzajem-

ne odniesienia. Wymaga to od osoby zajmującej się te-stowaniem i kompletowaniem dokumentacji bardzo du-żej staranności i systematyczności.

Przy testowaniu zgodności z wymaganiami należy zwrócić uwagę na to, że listy wymagań różnią się w za-leżności od regionu, do którego kierowana jest do wy-dania gra (szczególnie w zakresie używanej terminolo-gii i konfiguracji). Nieco inne dokumenty i procedury wy-magane są przy dystrybucji na rynki: europejski, amery-kański czy azjatycki.

Ponadto, oprócz wymagań systemowych, gry na omawiane konsole muszą zostać sklasyfikowane pod względem minimalnego wieku gracza. Wymaga to przeprowadzenia procesu certyfikacyjnego, który wią-że się z określeniem odpowiednich wymagań, które gra musi spełniać, aby być dostępna od odpowiednio ni-skiego wieku, jak i przygotowania dokumentacji (zarów-no pisemnej, jak i audiowizualnej) z tym związanej. Pro-ces ten i procedury także zależą od regionu czy nawet konkretnego kraju, gdzie gra ma być dystrybuowana.

Jak więc widać, proces testowania gier konsolowych jest nieco bardziej złożony, a przede wszystkim bardziej sformalizowany, niż gier na platformy mobilne, a także utrudniony z racji konieczności użycia specjalistyczne-go sprzętu testowego.

PodsumowanieW artykule przedstawiono niektóre aspekty charaktery-zujące testowanie gier na urządzenia mobilne i niektó-re konsole do gier. Zasygnalizowano, dlaczego testo-wanie jest potrzebne, kiedy ten proces rozpocząć, jak go sobie ułatwić, kto może go prowadzić. Obszerność tematu nie pozwala oczywiście przywołać wszystkich ważnych aspektów tego procesu, ale starano się zasy-gnalizować najważniejsze z nich.

W Sieci• http://javaveri�ed.com/ – strona domowa programu Java Verified;• http://www.symbiansigned.com – strona domowa programu Symbian Signed;• http://brew.qualcomm.com – strona domowa platformy BREW firmy Qualcomm;• http://developer.apple.com/iphone/program/ – strona iPhone Developer Program;• http://www.testerzy.pl/ – strona portalu testerzy.pl, zawierającego wiele użytecznych informacji;• http://www.deviceanywhere.com/ – strona usługi dostępu do telefonów Device Anywhere;• http://www.mob4hire.com/ – strona portalu mob4hire, łączącego developerów z testerami.

GRZEGORZ TARCZYŃSKIZwiązany z branżą testowania gier od 8 lat, uczestniczył w dziesiątkach projektów o różnej wielkości. Pracuje na sta-nowisku Managera działu Quality Assurance w �rmie Game-lion, wchodzącej w skład Grupy BLStream. Koordynuje i nad-zoruje pracę zespołu testerów nad wieloma projektami na platformach: JME, BREW, iPhone, Symbian, PC, Nintendo Wii, Nintendo DSi oraz Sony PSP.Kontakt z autorem: [email protected]

Page 58: SDJ 08 2010

8/201058

Z ŻYCIA ITOLOGA CMMI – Dlaczego powinno Cię to obchodzić?

www.sdjournal.org 59

Istnieje co najmniej kilka teorii, wyjaśniających powsta-wanie Wszechświata. Początkowo naukowcy twier-dzili, że wszechświat jest statyczny – raczej kurczy

się, niż rozszerza. Była to teza, z którą jednak wielu ko-smologów polemizowało. W końcu, za najbardziej praw-dopodobny uznano model ewolucji Wszechświata, który mówi o tym, iż ok. 13,75 mld lat temu miał miejsce Wielki Wybuch (ang. Big Bang), który zapoczątkował ekspansję Wszechświata. To był ten moment, w którym wszystko się zaczęło – i czas, i przestrzeń i grawitacja. W naszych co-dziennych zmaganiach z rozwojem oprogramowania na czas, zgodnie z zaplanowanym budżetem, a także nie-odbiegającego od pierwotnej „Książki Wymagań Klienta”, często dochodzimy do Momentu Krytycznego (Wielkiego Wybuchu). Symptomy bywają różne. Nawarstwiające się problemy, związane z jakością produktu: zbyt dużo błę-dów, zwłaszcza tych (o zgrozo!), wykrytych przez klien-ta; jak jest dużo błędów, ktoś je musi naprawiać; funkcjo-nalności nie działają tak, jak sobie tego zażyczył klient, który na domiar złego nagle zaczyna „wymyślać” i narze-kać. Do problemów, związanych z jakością oprogramo-wania dochodzą „porażki” menedżerskie: estymaty oka-zały się być wyssane z palca (projekt jest mocno niedo-

szacowany); harmonogram prac, który tak szczegółowo uzgadnialiśmy z klientem nagle bierze w łeb; do tego już na starcie widać, że budżet zostanie grubo przekroczony; no i ten klient… czego on właściwie chce? Ciągle dzwo-ni i zmienia swoje oczekiwania – jak w kalejdoskopie; w końcu nie wiemy już, które wymagania mają być fak-tycznie realizowane, a o których się tylko rozmawiało. Do tego gdzieś zapodziały się maile potwierdzające konkret-ne ustalenia z klientem, część rzeczy przecież załatwia-ło się przez skype. Statystyczny Menedżer zaczyna rwać włosy z rozpaczy. To przecież taki dobry klient, duża kor-poracja i w ogóle. Na domiar wszystkiego pojawiają się problemy z ludźmi, którzy zostali „zaprzęgnięci” do pro-jektu. Dziwnie, przestajemy mieć nad nimi kontrolę – na-rzekają, że przez te „wrzutki” od klienta, nie mogą normal-nie pracować. Kierownik Projektu z kolei przyciska śrubę. Wygląda na to, że niedoszacowaliśmy projektu. To nic, ludzie przecież mogą siedzieć po godzinach, no i mamy jeszcze weekendy. Tylko ten jeden raz, tylko na czas re-alizacji właśnie tego trudnego projektu.

Kiedy w nas coś pęknie? Kiedy właściwie zdiagnozu-jemy, że jesteśmy „chorzy”? Miałem klienta, który zwró-cił się do mnie z prośbą o pomoc dopiero po kilku latach

CMMI – Dlaczego powinno Cię to obchodzić?Pamiętam jak zaczynałem swoją przygodę z rozwiązywaniem sudoku. Na początku było niemiłosiernie trudno, potem stopniowo łapałem „wiatr w żagle”. Podobnie jest z modelem CMMI. Na pierwszy rzut oka wydaje się bardzo skomplikowany. Później, w miarę jak stopniowo go poznajemy, zaczynamy dostrzegać jego wewnętrzne „piękno” i logikę, widzimy że jego praktyki naprawdę mają sens i mogą nam się przydać. Tak było ze mną, i tak – jestem o tym przekonany – będzie również z Wami!

Dowiesz się:• Czym jest model CMMI i kto wpadł na ten genialny pomysł,

żeby go stworzyć• Jakie ma reprezentacje, a więc, czy zależy nam na zdobyciu

Górskiej Odznaki Turystycznej (GOT), czy medale zostawiamy żołnierzom.

• Która reprezentację powinna wybrać Twoja firma, jeśli już zde-cyduje się go wdrożyć

• Będzie wreszcie o tym, dlaczego warto

Powinieneś wiedzieć:• Na pewno przyda się świadoma wiedza o problemach, zwią-

zanych z jakością produktu, „porażkach” menedżerskich, które nam towarzyszą, no i o tym, co „misie lubią najbardziej” – pro-blemach z klientem. Ta wiedza w zupełności wystarczy, żeby zrozumieć, o co w tym wszystkim chodzi

Wielki Wybuch

Page 59: SDJ 08 2010

8/201058

Z ŻYCIA ITOLOGA CMMI – Dlaczego powinno Cię to obchodzić?

www.sdjournal.org 59

rusza się wyłącznie na ukos, w dowolnym kierunku, o do-wolną liczbę niezajętych pól, czy bardziej „z naszego po-dwórka” – podejmowane prace w projekcie powinny być dobrze oszacowane, zanim sporządzimy harmonogram prac), dzięki którym będzie możliwe wykonywanie okre-ślonych aktywności projektowych. Same procesy tworzą jednak tylko treść mapy – są jej obiektami oraz ich wza-jemnym rozmieszczeniem. Mapa pokazuje jak do nich dotrzeć. Dlatego, myśląc poważnie o poprawie procesów wytwórczych w organizacji, należy w pierwszej kolejności pomyśleć o dobrej mapie.

Model dla wszystkichNasze poszukiwania „dobrej mapy” mogą się sprowa-dzić do dwóch opcji: albo ją stworzymy sami, jeśli oczywi-ście posiadamy odpowiednią wiedzę geodezyjną lub ku-pimy tzw. „gotowca”, który został opracowany przez od-powiednią grupę ekspertów, dobrze znających się na rze-czy. Przykładem takiego „gotowca”, przeskakując już do ogródka informatycznego, jest Capability Maturity Model Integration, w skrócie CMMI. CMMI, jak można łatwo za-uważyć, nie jest modelem, który można spotkać na okład-ce jednego z numerów „Men’s Health”. Nie dla wszystkich odpowiednia budowa mięśni, połączona z solidną daw-ką solarycznego brązu – są właściwym punktem odnie-sienia. W inżynierii oprogramowania pojęcie „modelu” ma zupełnie przeciwne znaczenie. Można powiedzieć, iż jest to pewien typ idealny – „miejsce” przez wszystkich uzna-ne za cenne i wartościowe, w którym warto być, a nawet się zadomowić. Jestem przekonany, iż każda organiza-cja IT, która zapozna się (choćby pobieżnie) z praktykami modelu CMMI, uzna, iż są celem, który warto osiągnąć.

CMMI powstał w latach 80., z inicjatywy Departamentu Obrony Stanów Zjednoczonych (ang. Department of De-fense, w skrócie DoD), który w tym czasie borykał z pro-blemami wyboru właściwych poddostawców i zlecania im w części lub całości realizowanych prac. Jak można się domyśleć sprawa raczej nie była trywialna: mamy la-ta osiemdziesiąte i standardy, dotyczące jakości dopiero się rodziły (w latach siedemdziesiątych pojawił się artykuł Rona Royce’a, który po raz pierwszy opisał model kaska-dowy). Zdecydowano, iż prace, dotyczące opracowania kryteriów wyboru poddostawców zostaną zlecone grupie amerykańskich programistów, pracujących w Software Engineering Institute (w skrócie SEI), przy Carnegie Mel-lon Univeristy w Pittsburghu. Instytut jest do dzisiaj spon-sorowany przez DoD i do dzisiaj kontynuuje swoją pier-wotną działalność w zakresie rozwijania modelu, choć na nieco szerszą skalę.

Efektem prac Software Engineering Institute był tzw. Kwestionariusz Oceny Dojrzałości Procesów (ang. Ma-turity Questionnaire), który składał się z 85 pytań, doty-czących między innymi takich zagadnień jak: planowa-nie projektu, śledzenie postępu prac, zarządzanie har-monogramem, zarządzanie wymaganiami, zarządzanie

od czasu wystąpienia pierwszych „objawów choroby”. Zmiany, które musiał wprowadzić w swojej firmie, głów-nie w zakresie reguły współpracy z klientem (zarządzanie projektem, zarządzanie wymaganiami, zarządzanie kon-figuracją), kosztowały go bardzo dużo. Jego organizacja zapłaciła wysoką cenę. Najważniejsze jednak, że Wielki Wybuch nastąpił.

Zagubieni na wyspieOpisane powyżej „symptomy choroby” można oczywiście leczyć na różne sposoby. Według mnie, jednym z lep-szych jest próba zrozumienia aktualnie istniejących pro-cesów w firmie lub ich ewentualnego braku. Doskonale pamiętam początki swojej kariery zawodowej, kiedy wy-dawało mi się, że jestem świetnym Kierownikiem Pro-jektu, i nie ma takiego żywiołu, który byłby wstanie za-wrócić okręt prowadzonego przeze mnie przedsięwzię-cia, z pierwotnie obranego kursu. Nic bardziej mylnego. Bez odpowiedniej mapy i kompasu, nie byłem w stanie dotrzeć „drogą morską z Europy do Indii”. Czułem się jak jeden z 71 rozbitków – bohaterów amerykańskiego seria-lu „Zagubieni”, którzy bezskutecznie próbowali wydostać się z wyspy, na której się znaleźli w wyniku katastrofy lot-niczej feralnego lotu 815. Brakowało mi „mapy i kompa-su” – zbioru konkretnych reguł, kroków i działań, tworzą-cych coś, co w obiegowej opinii nazywamy „procesami”. Umiejętne zwrócenie uwagi na świat procesów w organi-zacji jest doskonałym remedium na pojawiające się pro-blemy.

Znaleźć dobrą mapęDobrze zdefiniowane procesy są w stanie pomóc or-ganizacji zapanować nad ludźmi, przypisanymi do po-szczególnych projektów (ich doświadczenie, inwestycja w kosztowne szkolenia nie zawsze wystarczą, podob-nie jak intensyfikacja pracy). Podobnie jest, gdy chodzi o stosowanie określonych technologii. Dobrze przemy-ślane procesy – na przykład te, które dotyczą projekto-wania architektury systemu, czy też rozwijania i integra-cji jego poszczególnych komponentów – zwiększają sku-teczność stosowania określonych technologii. Weźmy na przykład norweskiego trębacza Nilsa Molvaera, kojarzo-nego z nurtem nu-jazzu, właściwie jednego z jego pio-nierów. Jakież on potrafi wydobywać dźwięki z tego in-strumentu; niektórzy określają jego technikę jako „voicing trumpet”, gdyż wydobywany przez niego dźwięk przypo-mina momentami głos ludzki. Świetna technika w kon-tekście dobrze zdefiniowanego otoczenia procesowego. Wystarczy jednak wyobrazić sobie Molvaera jako człon-ka sekcji dętej orkiestry OSP w Kopydłowie (fikcyjna miej-scowość), żeby zrozumieć dlaczego stosowanie określo-nej technologii IT musi być osadzone w odpowiednim kontekście procesowym.

Wdrożenie określonych procesów w firmie jest ufundo-wane wdrożeniem określonych reguł gry (np. goniec po-

Page 60: SDJ 08 2010

8/201060

Z ŻYCIA ITOLOGA CMMI – Dlaczego powinno Cię to obchodzić?

www.sdjournal.org 61

konfiguracją, zapewnienie jakości tworzonego oprogra-mowania. Pytania te dotyczyły jednocześnie najważniej-szych procesów, które powinny być zaimplementowane i wykorzystywane w każdym projekcie informatycznym. SEI, używając swojego kwestionariusza, przebadał set-ki firm, dochodząc przy tym do ciekawych wniosków. Jed-nym z ważniejszych było stwierdzenie, iż proces „dojrze-wania” do określonego poziomu jakości i produktywno-ści miał podobny przebieg w każdym z badanych przy-padków. Wszystkie wnioski zostały zebrane i opubliko-wane w formie tzw. Raportu Technicznego (ang. Techni-cal Report), który dał początek pierwszej wersji Capabi-lity Maturity Model for Software (CMM-SW). Sam model przeżył wiele transformacji, z których chyba najważniej-szą jest ta, że w trakcie prac rozwojowych wyodrębnio-ne zostały tzw. „konstelacje” modelu, dzięki którym moż-liwe jest jego zastosowanie w różnych obszarach, a więc nie tylko w inżynierii oprogramowania (CMMI-DEV), ale i w organizacjach prowadzących działalność serwisową (CMMI-SVC), a także akwizycyjną (CMMI-ACQ). Cieka-wym pomysłem SEI, jest również próba zwrócenia uwagi na praktyki, związane z zarządzaniem personelem (Pe-ople CMM), którym warto poświęcić odrębną publikację, zwłaszcza w kontekście wszechobecnej dzisiaj „mody” (której przyznaję również sam uległem) na tzw. „zwinne” metod tworzenia oprogramowania (agile).

Jak dojść do schroniska?Ktokolwiek chce wykorzystać model CMMI do poprawy procesów w swojej firmie, powinien traktować go właśnie w kategoriach mapy, jak to zostało już wcześniej podkre-ślone. Wydaje mi się, że porównanie go do mapy bar-dzo trafnie oddaje sposób, w jaki powinno się z niego ko-rzystać. Mapa pozwala nam dotrzeć w określone miej-sce. W pewnym sensie wyznacza kierunek. Posiadają na przykład mapę Beskidu Żywieckiego wiem, że do schro-niska na Hali Krupowej możemy dojść z Babiej Góry, By-strej Podhalańskiej, Jordanowa, Juszczyna, Kojszówki, schroniska na Luboniu Wielkim etc. Mapa nie mówi, mu-sisz iść tędy, żeby osiągnąć cel. Z drugiej strony ma ja-sno zdefiniowane cele w postaci konkretnych schronisk. I taki jest w przypadku modelu CMMI, który składa się określonej liczby celów (schronisk), które możemy osią-gnąć za pomocą konkretnych praktyk (dobrych i spraw-dzonych tras na mapie). Przez „dobre praktyki” rozumiem tutaj konkretne działania, które wielokrotnie sprawdziły się w prowadzeniu projektów informatycznych na całym świecie. Należy bowiem pamiętać, iż CMMI nie jest wy-tworem „fantazji intelektualnych” kilku naukowców z Pen-sylwanii, którzy nie wiedzieli jak zagospodarować pienią-dze z grantu, ale jest wynikiem pracy i zaangażowania praktyków z firm informatycznych (i nie tylko) na całym świecie. Dlatego też ten model tak intensywnie się rozwi-ja. Jest to zrozumiałe, biorąc pod uwagę w jakim tempie rozwija się sama inżynieria oprogramowania.

Model CMMI ma dość złożoną strukturę, jak dobra wojskowa mapa. Dlatego przedstawię ją tylko w bardzo ogólnym zarysie. Składają się na nią następujące kom-ponenty:

• Poziomy Dojrzałości (ang. Maturity Levels) w Repre-zentacji Stałej oraz Poziomy Wydolności (ang. Capa-bility Levels) w Reprezentacji Ciągłej (o reprezenta-cjach opowiem za chwilę)

• Obszary Procesowe (ang. Process Areas)• Cele – Ogólne (ang. Generic) i Specyficzne (ang.

Specific)• Praktyki – Ogólne (ang. Generic) i Specyficzne (ang.

Specific)

Na pierwszy rzut oka, przypomina to trochę znane wszystkim „dzielenie dzidy bojowej”. Dlatego też zachę-cam Czytelnika, żeby na tym etapie nie zaprzątał sobie tym głowy. Natomiast rzeczą chyba najbardziej istotną przy pierwszym spotkaniu z modelem CMMI jest fakt, iż jest to w zasadzie ogromny w worek z Obszarami Proce-sowymi (nie mylić z procesami!), które mówią jak dojść do schroniska, możliwie najprostszą drogą.

Zdobywanie odznakiTutaj dochodzimy do bardzo ważnego aspektu mode-lu CMMI – jego reprezentacji. A więc, czy zależy nam na zdobyciu Górskiej Odznaki Turystycznej (GOT), czy me-dale zostawiamy żołnierzom. Do dzisiaj pamiętam swoją książeczkę GOT, i to jak pieczołowicie zbierałem punkty, żeby zdobyć kolejno: „popularną”, „małą brązową”, „ma-łą srebrną” i wreszcie… „małą złotą” odznakę turystycz-ną. Specjalnie wybierałem na mapie takie trasy (było to zależne zupełnie ode mnie), żeby uzbierać jak najwięcej punktów. Najgorsze w tym wszystkim było to, że odzna-ki trzeba było zbierać w odpowiedniej kolejności, tzn. nie mogłem na przykład z „popularnej” przeskoczyć na „ma-łą złotą”.

Pierwsza z dwóch reprezentacji modelu CMMI to Re-prezentacja Stała (ang. Staged). Używając przytoczonej wcześniej metafory, jest to proces zdobywania odznaki turystycznej. Wszystkie Obszary Procesowe modelu zo-stały podzielone na określone etapy, zwane „Poziomami Dojrzałości” (ang. Maturity Levels), które muszą być ko-lejno „zdobywane” (jak odznaki turystyczne), żeby osią-gnąć określony stopień zaawansowania procesów wy-twórczych w organizacji. Model CMMI wyróżnia pięć Po-ziomów Dojrzałości, i co ważne, podobnie, jak nie może-my najpierw zdobyć „popularnej”, a potem od razu „małej złotej” odznaki turystycznej, tak i tutaj – przedsiębiorstwa muszą się wznosić na szczyty małymi krokami. Najpierw Poziom 2., potem Poziom 3. etc. Nie ma mowy o prze-skoczeniu, na przykład z Poziomu 2. od razu na 5. Przy-znaję, że jest to bardzo wygodne podejście i bardzo czę-sto zachęcam Klientów, żeby z niego skorzystali, zwłasz-

Page 61: SDJ 08 2010

8/201060

Z ŻYCIA ITOLOGA CMMI – Dlaczego powinno Cię to obchodzić?

www.sdjournal.org 61

cza jeżeli organizacje dopiero zaczynają swoją przygodę z poprawą procesów wytwórczych, i nie mają odpowied-niego doświadczenia i wiedzy, żeby to zrobić „bez ma-py”. Poza tym, Reprezentacja Stała jest w pewnym stop-niu intuicyjna. Czujemy przez skórę, że jeżeli nie mamy uporządkowanych podstawowych procesów, związanych z zarządzaniem projektami (Poziom 2.), nie ma sensu dy-wagować, jak moglibyśmy statystycznie kontrolować pro-cesy w naszej firmie (Poziom 4.) – choć nie ukrywam, jest to bardzo pasjonujący i wciągający temat.

Druga reprezentacja, to Reprezentacja Ciągła (ang. Continuous). Tutaj sprawa wydaje się dość prosta. Nie potrzebne mi są żadne odznaki turystyczne. Chcę po prostu dojść do schroniska, trasą którą uwielbiam, i nie wyobrażam sobie, żebym mógł zrobić to inaczej. Można i tak, ale w tym wypadku trzeba już coś nieco wiedzieć o górach, przynajmniej gdzie chcemy dojść, do jakiego schroniska – resztę załatwi nam mapa (która w obu re-prezentacjach jest niezbędna). Reprezentacja Ciągła da-je firmie duże możliwości manewru, jeśli chodzi o prakty-ki związane z poprawą istniejących w niej procesów. To firma sama, jak turysta znający „swoje” góry, decyduje o tym, które z istniejących procesów wymagają popra-wy. Tutaj nie mamy do czynienia z konkretnym „przepi-sem na sukces”. W Reprezentacji Ciągłej pojawiają się tzw. Poziomy Wydolności (ang. Capability Levels), któ-rych jest sześć (pojawia się poziom zerowy, liczony ja-ko dodatkowy szósty) i które mają zastosowanie tylko (!) do wybranego procesu, który chcemy doskonalić. Pro-szę zwrócić uwagę na tę subtelną różnicę pomiędzy re-prezentacjami. W pierwszej (Stałej) jest 5 Poziomów Dojrzałości, z których każdy jest zbiorem procesów, któ-rych odpowiednie wdrożenie gwarantuje firmie osiągnię-cie określonej dojrzałości wytwórczej. W drugiej (Ciągłej) natomiast, nie ma wcześniej zdefiniowanej ścieżki roz-woju (doskonalenia); w związku z czym, sami musimy wiedzieć, co tak naprawdę chcemy poprawić (jaki pro-ces) i w jakim stopniu (Poziomy Wydolności). Tutaj sami wybieramy drogę do schroniska. Może być ona krótsza lub dłuższa; przez las lub z punktami widokowymi. Jak się łatwo domyśleć, jest to bardzo praktyczne rozwiąza-nie dla tych organizacji, które mają już określoną wiedzę na temat swoich procesów wytwórczych – znają swoje mocne i słane strony oraz wiedzą, co chciałyby w nich zmienić i poprawić.

Dlaczego powinno Cię to obchodzićOsobiście nie lubię pokazywać „ładnych” statystyk (np. Raporty Techniczne regularnie publikowane przez SEI), które rzekomo mają spełniać rolę ostatecznego dowodu na to, że wdrożenie modelu CMMI faktycznie się opła-ca. Dużo bardziej wolę pozytywne przykłady „z własnego podwórka”. No właśnie, i z tym jest mały problem. Gene-ralnie firmy niewiele mówią o swoich sukcesach, związa-nych z wdrożeniem modelu CMMI. Wiem to, z własnego

doświadczenia jako konsultanta, gdy niejednokrotnie „bła-gałem” klientów o wystąpienie i podzielenie się swoimi sukcesami w zakresie doskonalenia procesów wytwór-czych na tej lub innej konferencji informatycznej. W wie-lu przypadkach uzyskiwałem odpowiedź odmowną, sły-sząc wymówki w stylu: nie mam czasu, mam wyjazd służ-bowy, może i mógłbym, ale czy jest się czym chwalić etc. Przyznam szczerze, iż nie bardzo wiem z czego to wyni-ka. Z jednej strony rozumiem Menedżerów, pracujących w dużych korporacjach, gdzie procedury dot. upublicznia-nia wewnętrznych danych firmy są tak skomplikowane, a uzyskanie stosownego „zezwolenia” wymaga kontaktu z trzema odrębnymi działami w firmie (najczęściej zloka-lizowanych w różnych krajach), że nikt nie ma sił i czasu, żeby temu wszystkiemu stawić czoło. Z drugiej strony, ist-nieje duża grupa firm, która po prostu nie chce się dzielić pozytywnymi przykładami i praktykami, które pomogły im uzdrowić sytuację w ich firmie. Próbując to jakoś zmienić, czuję się trochę jak Tom Hanks, w filmie „Cast Away”, któ-ry wraz z niezawodnym Panem Wilsonem (piłką do siat-ki), próbuje stawić czoło emocjonalnym wyzwaniom izo-lacji – bezradny.

Zastosowanie modelu CMMI nie jest ograniczone do konkretnego sektora. Z powodzeniem mogą go stoso-wać wszystkie organizacje, które mają cokolwiek do czynienia z tworzeniem oprogramowania (np. dedyko-wane wewnętrzne Działy IT w danej firmie). CMMI moż-na więc wdrażać w organizacjach transportu publiczne-go, administracji rządowej, sektora finansów i ubezpie-czeń, ochrony zdrowia, i wielu innych. Dodatkowo wdro-żenie CMMI nie zależy od rozmiaru organizacji. Prakty-ki modelu można więc implementować zarówno w du-żych gigantach korporacyjnych, jak i małych i średnich przedsiębiorstwach (nawet, jeśli zatrudniają mniej niż 25 pracowników). Wykorzystując moje osobiste doświad-czenia, chciałbym zwrócić uwagę na zaledwie kilka ob-szarów, w które po wdrożeniu modelu CMMI zyskają na znaczeniu i zyskają uznanie i podziw w oczach Państwa klientów:

• Planowanie Projektu – absolutny faworyt na mojej li-ście. Już po wdrożeniu kilku prostych praktyk mo-delu, życie prowadzonych projektów zmienia się o 180 stopni. Nagle poczujecie, że zaczynamy sto-sować odpowiednie techniki szacowania parame-trów projektu (jak np. Wideband Delphi, Planning Po-ker, Punkty Funkcyjne itp.), a Kierownik Projektu po-trafi nagle sporządzić realny harmonogram prac, wy-korzystując do tego wcześniej stworzonego WBS-a i estymaty Zespołu.

• Monitoring i Kontrola Projektu – czyli „Uśmiechnię-ty Klient”. Każdy Kierownik Projektu wie, co to ozna-cza: dobra reputacja, cenne rekomendacje oraz du-że prawdopodobieństwo na kontynuację współpracy. Wdrożenie określonych praktyk modelu sprawi, że

Page 62: SDJ 08 2010

8/201062

Z ŻYCIA ITOLOGA

nasz klient będzie czuł się naprawdę „dopieszczony”, przynajmniej w obszarze wymiany informacji (raporty tygodniowe, miesięczne przeglądy projektów, cyklicz-ne spotkania interesariuszy projektu, demonstracje produktu, wyniki przeprowadzonych testów etc.).

• Zarządzanie Wymaganiami – przestaniemy narze-kać, że nie panujemy nad tym, czego oczekuje od nas klient. Wymagania będą zarządzane, bardzo możliwe że w konkretnym narzędziu (za które nieko-niecznie będziemy musieli zapłacić krocie), będzie-my mieli dokument opisujący życzenia klienta, a tak-że ich „tłumaczenie” na język wymagań technicz-nych, no i będziemy wreszcie kontrolować zmiany do wymagań, a także śledzić, które z nich zostało prze-testowane, a które „stoi w kolejce” (matryca powią-zań wymagań).

• Zarządzanie konfiguracją – zdarzyło mi się raz dora-dzać Klientowi, który był poddostawcą dwóch dużych koncernów samochodowych, stanowiących dla sie-bie wzajemną konkurencję. Już na samym początku trochę zdziwiło mnie, dlaczego klient tak bardzo naci-ska akurat na „uzdrowienie” procesów z obszaru za-rządzania konfiguracją. Okazało, że na skutek bała-ganu właśnie w tym obszarze, podczas ostatniego wydania produktu, jeden z koncernów (przez przy-padek o zgrozo!) otrzymał kod źródłowy swojej kon-kurencji. CMMI robi na tym polu bardzo dobrą robotę i pozwala uniknąć takich stytuacji. Pliki są poprawnie wersjonowane (zgodnie z jednym, ogólnie obowiązu-jącym standardem), zmiany w elementach konfigura-cji są monitorowane, na kolejnych etapach prac roz-wojowych tworzone są odpowiednie „punkty odnie-sienia (ang. baselines), które umożliwiają „kontrolę” rozwijanego oprogramowanie.

• Miary i analizy – nagle zaczynami się interesować danymi, które mogą coś znaczyć. Metryki to bardzo bogaty świat, który znakomicie wspiera zarządza-nie prowadzonym projektem. Oto, pojawią się dane, których wcześniej w ogóle nie zauważaliśmy, jak na przykład: dokładność estymat, liczba błędów zgła-szanych przez testerów i klienta; rozmiar powstają-cego kodu, produktywność, liczba przypadków te-stowych, które się nie wykonały etc. Dane zaczynają być wykorzystywane do lepszego zarządzania pro-jektem. Analizowane (!) są przyczyny wystąpienia określonych problemów, menedżerowie definiują ak-cje korekcyjne.

Czytelników zainteresowanych bardziej „twardymi” da-nymi w zakresie efektów wdrożenia modelu CMMI w organizacji, odsyłam do Raportu Technicznego SEI (CMU/SEI-2003-SR-009, do pobrania na stronie: http://www.sei.cmu.edu/), który zawiera liczne przypadki uży-cia w takich firmach jak: Accenture, Boeing, Lockheed Martin, Northrop Grumman, czy Thales. Dodatkowo od-

syłam do kilku przypadków użycia z „naszego podwór-ka”, przedstawionych w mojej książce: „CMMI – Dosko-nalenie Procesów w Organizacji” (Wydawnictwo Nauko-we PWN 2010) z firm: GE Money Bank, GMC Software Technology oraz Hewlett Packard (Global Delivery Po-land Center).

O tym i innych artykułach Prezentowany artykuł jest pierwszym z cyklu, który – pa-rafrazując tytuł jednej z książek Leszka Kołakowskiego – można by potraktować jako „mini-wykłady o maxi spra-wach”. Osoby, które miały jakąkolwiek styczność z mode-lem CMMI, jego językiem (przypominającym momentami utwory Goeffreya Chaucera) – wie, jak wiele wysiłku po-trzeba, żeby się go nauczyć, zrozumieć i właściwie zin-terpretować; nie mówiąc już o jego wdrożeniu. Dlatego, zwróciłem uwagę tylko na kilka najważniejszych aspek-tów tego modelu, które – moim zdaniem – każdy począt-kujący adept powinien na jego temat wiedzieć. Świado-mie, nie wchodziłem w szczegóły związane z architek-turą modelu, jak i trudną terminologią SEI, która sprawia problemy nawet najbardziej wytrawnym znawcom. Zain-teresowanych poznaniem więcej szczegółów oraz możli-wych przypadków zastosowania w praktyce, odsyłam do mojej książki, którą wcześniej przytaczałem. Artykuł koń-czą pozytywne przykłady z „mojego podwórka”, przema-wiające na rzecz, jeśli nie wdrożenia, to przynajmniej krót-kiej refleksji nad tym, czy model CMMI powinien nas fak-tycznie obchodzić.

W kolejnych artykułach pojawią się tematy związa-ne z możliwością symbiozy modelu CMMI na gruncie metody SCRUM, która ostatnio jest bardzo popularnym i nowatorskim podejściem do zarządzania pracą w pro-jektach. Dodatkowo będą poruszę te zagadnienia inży-nierii oprogramowania, które – jak mawiał jeden z mo-ich szefów – są „pilne i ważne”, a więc nie sposób ich pominąć.

MARIUSZ CHRAPKO Mariusz Chrapko jest wieloletnim praktykiem w zakresie do-skonalenia procesów tworzenia oprogramowania w oparciu o model CMMI®. Wspiera �rmy informatyczne na terenie ca-łej Europy, prowadząc coaching zespołów projektowych oraz szkolenia na różnych szczeblach organizacyjnych. Dodatkowo jest praktykiem we wdrażaniu i adaptacji metod Agile Softwa-re Development (wcześniej Agile Coach/ Centrum Oprogramo-wania Motoroli w Krakowie), Programu Metryk Organizacyj-nych, a także procesu Peer Review. Jest autorem wielu publika-cji z zakresu inżynierii oprogramowania oraz prelegentem na konferencjach krajowych i międzynarodowych. Autor pierw-szej w Polsce książki na temat modelu CMMI raz jego praktycz-nego zastosowania. Od roku 2009 współpracuje z �rmą LOY-CON® business solutions, udzielając wsparcia �rmom informa-tycznym w regionie Europy Centralnej i Wschodniej. Kontakt z Autorem: [email protected]

Page 64: SDJ 08 2010

8/201064

WYWIAD Rozmowa z Mariuszem Chrapko

www.sdjournal.org 65

SDJ: Skąd pomysł na książkę o modelu CMMI?MCH: Pamiętam, jak zaczynałem swoją przygodę

z CMMI (wtedy był to jeszcze CMM for Software, CMMI powstał kilka lat później), a więc jakieś siedem lat temu, i próbowałem czegokolwiek dowiedzieć się na temat mo-delu z naszych polskich opracowań. Efekt był taki, że udawało mi się odnaleźć jakieś, mniej lub bardziej spój-ne, wystąpienia konferencyjne – raczej mocno teoretycz-ne, które sprawiały wrażenie trochę „pisanych na kola-nie”, ale chyba najbardziej mnie denerwował brak wspól-nego słownika. Otóż poszczególne komponenty mode-lu, praktycznie w każdym wystąpieniu były inaczej tłu-maczone. Było to bardzo frustrujące. Co więcej, przez te zgoła siedem lat, do momentu wydania mojej książ-ki niewiele się zmieniło. Do dzisiaj, mając do wyboru ory-ginał lub jego polskie tłumaczenie, zawsze wybieram to pierwsze. Bałagan pojęciowy jest spotykany nawet w jed-nym i tym samym wydawnictwie. Przykład: dwa tłuma-czenia znanych podręczników do inżynierii oprogramo-wania. Dodatkowo dochodzi problem samych tłumaczy, którzy w ogromnej większości przypadków są brani „z uli-cy” i nie zawsze mają, choćby minimalną, wiedzę na te-maty IT. Koniec końców, efekt jest taki, że pracownicy firm informatycznych, czytając taką książkę, w ogóle nie wie-dzą, o co chodzi. SDJ: A jak Pan sobie poradził z problemem języka

branżowego?MCH: Ja miałem trochę „łatwiej”. Przed napisaniem

książki „siedziałem” już przez pewien czas w temacie, wgryzłem się w żargon programistów, wiedziałem, jak się z nimi komunikować. Pamiętam, jak jeszcze pracu-jąc w krakowskiej Motoroli, napisałem maila, w którym użyłem słowa „testy jednostkowe”. Natychmiast dostałem zwrotkę: o jakie testy ci chodzi? – chodziło oczywiście

o „unit testy” (śmiech) – bo tak się właśnie mówiło. Do te-go dochodzi cała masa innych słów: „effort”, „stub”, „ba-seline” (klasyka gatunku), plus spolszczenia, które rzadko kiedy znajdzie Pan w słowniku (np. „impakt”, „dependen-cje”). Natomiast, oprócz języka – drugim, bardzo ważnym dla mnie motywatorem do napisania książki o CMMI by-li pracownicy firm, którym doradzałem. Przy różnego ro-dzaju szkoleniach czy coachingu często padało pytanie, czy jest coś po polsku na ten temat. Teraz jest! (śmiech)SDJ: Poza normami CMMI, dotyczącymi jakości proce-

su wytwarzania oprogramowania, jest jeszcze seria norm ISO/IEC 25000 SQuaRE dotyczących jakości samego produktu programowego (jakości wewnętrznej, zewnętrz-nej oraz jakości w użyciu). W jaki sposób jakość proce-su wytwarzania oprogramowania wpływa na samą jakość produktu programowego?MCH: Istnieje szerokie grono osób, którzy twierdzą, że

jakość produktu jest czymś zupełnie niezależnym od ja-kości procesu tworzenia oprogramowania. No bo prze-cież, koniec końców, naszych klientów bardziej interesu-je to, czy software, który mu dostarczamy, jest niezawod-ny (ang. realiability), użyteczny (ang. usability), wydajny (ang. efficiency), łatwy w utrzymaniu (ang. maintainabili-ty), przenośny (ang. portability), niż to, w jakim sposób on powstaje. Klient płaci za jakość produktu, cała reszta jest tylko „czarną skrzynką”, która z punktu widzenia klienta niewiele znaczy (dla niektórych firm nawet lepiej jest, że do niej nie zagląda). Pomijając jednak, mniej lub bardziej złożone, dyskusje na ten temat, osobiście uważam, że ja-kość procesu wytwórczego ma duży wpływ na jakość wy-twarzanego oprogramowania. Ostatnio czytałem zabaw-ną książkę Anthony’ego Bourdaina „Kill Grill. Restauracja od kuchni”, po której to lekturze dwa razy zastanawiam się zanim wybiorę posiłek w restauracji na niedzielny obiad

Rozmowa z Mariuszem Chrapko

Page 65: SDJ 08 2010

8/201064

WYWIAD Rozmowa z Mariuszem Chrapko

www.sdjournal.org 65

starczanych produktów. Może się na przykład okazać, że nasi pracownicy zaczną nagle poruszać się w rzeczywi-stości, żywcem przypominającej jedną z powieści Franza Kafki – i nagle zabraknie czasu na pisanie dobrego kodu. Drugi przypadek – nazwijmy go „leniucha malarza” pole-ga na wdrożeniu modelu i nie podejmowaniu żadnych dal-szych działań, związanych z jego „utrzymaniem” w firmie. I nie chodzi tutaj o to, że brakuje nam zaufania do ludzi i trzeba ich pilnować, żeby „podążali” szlakiem przyjętych standardów. Ostatnio wdrażałem praktyki CMMI, zwią-zane z „Zarządzaniem Konfiguracją” w jednej z polskich firm IT, i pamiętam jedno ze spotkań projektowych, pod-czas którego długo rozmawialiśmy o konieczności wpro-wadzenia tzw. punktów odniesienia (ang. baselines), któ-re pomogłyby nam utrzymać porządek w kodzie źródło-wym, przechowywanym w SVN-ie. Dla „niewtajemniczo-nych” dodam, że jest to narzędzie do kontroli wersji opro-gramowania. Przyjęliśmy więc określoną strategię nada-wania etykiet konkretnym wersjom kodu, które nie były aż tak bardzo skomplikowane, ale wymagały (przynajmniej w początkowym okresie stosowania) osoby, która co ja-kiś czas sprawdzałaby, czy etykiety są dobrze nadawane. Wszyscy jednogłośnie uznaliśmy, że takie „utrzymanie” naszej praktyki jest niezbędne, żeby się w tym wszyst-kim odnaleźć. A proszę zauważyć, że była to tylko jed-na z praktyk modelu CMMI, którą trzeba było „utrzymać”. Jeżeli wdrażamy więc na przykład 2. Poziom Dojrzałości, wówczas jest ich dużo więcej, i bez właściwej „pielęgnacji” bardzo szybko może się okazać, że straciliśmy nie tylko czas i pieniądze, ale i nasze wysiłki w żaden sposób nie miały wpływu na jakość dostarczanych produktów.SDJ: A teraz pytanie związane z Pana doświadcze-

niem w zakresie wdrażania programów metryk. Czy mo-że Pan podać przykłady najczęściej używanych metryk i uzasadnić dlaczego akurat na nie pada wybór?MCH: Są to na pewno metryki, związane z zarządza-

niem błędami w projektach IT (tj. liczba wykrytych błę-dów wg stopnia ważności, metryka gęstości błędów, licz-ba błędów wykrytych przez klienta, metryka zaległości błędów, a więc ile błędów mamy jeszcze do rozwiąza-nia). Druga grupa to metryki, dotyczące testów, a więc przede wszystkim metryka tzw. „Krzywej Testów”, która pokazuje: ile przypadków testowych zostało wykonanych względem przyjętego planu; ile spośród nich „nie prze-szło”, a ile, z różnych przyczyn, zostało zablokowanych. Wreszcie bardzo przydatne i chyba najczęściej stosowa-ne są metryki dotyczące przebiegu projektu, takie jak: do-kładność estymat czy pracochłonność (ang. effort). SDJ: Panuje opinia, że praktyki Agile zdają egzamin tyl-

ko wśród doświadczonych zespołów. Jakie doświadcze-nia ma Pan podczas wdrażania praktyk Agile w firmach posiadających słabo wyszkolonych pracowników IT?MCH: Agile to przede wszystkim praca zespołowa, i je-

żeli ktoś „odstaje”, zespół to wcześniej czy później za-uważy i w naturalny sposób „wyeliminuje”. Mam tutaj

(śmiech). W swojej książce bynajmniej nie wybiela bran-ży gastronomicznej, pokazując raczej „mroczne” zaka-marki nowojorskiej gastronomii. Moim zdaniem książka, w sposób rewelacyjny ilustruje postawioną przeze mnie wcześniej tezę: jakość oprogramowania zależy od jako-ści procesu jego wytwarzania. Wracając natomiast do ze-stawienia modelu CMMI i serii norm ISO/IEC 25000 SQu-aRe… Otóż według mnie, model CMMI jest „bardziej po-jemny”. Zawiera 22 Obszary Procesowe (nie mylić z pro-cesami!), które kondensują szereg dobrych praktyk inży-nierii oprogramowania, dotyczących różnych dziedzin ży-cia organizacji oraz prowadzonych w niej projektów. Stąd można powiedzieć, iż model CMMI, ze względu na swoją kompleksową „siłę rażenia”, wskazuje również na aspek-ty, związane z jakością produktów, o których mówi przy-toczona przez Pana seria norma ISO/IEC 25000 SQu-aRE. W tym, i wielu innych przypadkach (np. Six Sigma, Scrum, ITIL, COBIT, ISO 9001) model CMMI wykazuję bardzo dużą skłonność do symbiozy. Dlatego, gdy pró-bujemy go porównywać z jakimikolwiek innymi modela-mi czy standardami – wcześniej czy później zawsze doj-dziemy do wniosku, że jest on komplementarny z porów-nywanym podejściem. SDJ: Czyli, jak dobrze rozumiem, normy serii ISO/IEC

2500 SQuaRE, Scrum czy Six Sigma pomagają osiągnąć założone przez niego cele i praktyki, a nie działają na je-go niekorzyść?MCH: Dokładnie tak. Kilka lat temu, na europejskiej

wersji konferencji SEPG, miał ciekawą prezentację Dave Zubrov, który mówił właśnie na temat tego, jak normy se-rii ISO/IEC 2500 mogą wspierać model CMMI. Przy odro-binie szczęścia można się do niej „dogrzebać” na oficjal-nej stronie www.sei.cmu.eduSDJ: Czy jest możliwe, że mimo pełnego wdrożenia

CMMI końcowy produkt programowy nie osiąga zadowa-lającej jakości?MCH: Oczywiście, że tak. Po pomalowaniu domu mo-

że się nagle okazać, że kolor „Radość w rozkwicie”, któ-ry miał nadać wnętrzu naszego mieszkania „pogodę du-cha i optymizm ukwieconej łąki”, nagle okazał się być nie-trafiony – zbyt odważny, i wolelibyśmy coś bardziej spo-kojnego i eleganckiego, np. „Lekko jak z płatka”, kompo-zycja delikatnego beżu, alabastru i migdałów. Albo zrobi-liśmy malowanie, wszystko gra, mija pięć lat i wypadało-by trochę (!) odświeżyć ściany, i jakoś ciągle nam się nie składa. I tak mija sześć, siedem, osiem lat od pierwsze-go malowania i… pierwotne piękne kolory naszych ścian pokrywa szary osad, który niczym tornado F4 pochłania kolejne wnętrza naszego mieszkania. Myślę, że te dwa przykłady bardzo dobrze ilustrują, z jakimi konsekwencja-mi należy się liczyć, podejmując decyzję o wdrożeniu mo-delu CMMI we własnej organizacji. W pierwszym przy-padku, jeżeli nie zaplanujemy w sposób właściwy ścież-ki poprawy procesów wytwórczych, jak najbardziej może-my spodziewać się negatywnego wpływu na jakość do-

Page 66: SDJ 08 2010

8/201066

WYWIAD

sporo przykładów, że to faktycznie działa. Dlatego, mo-im zdaniem, zupełnie nieuzasadnione jest twierdzenie, iż praktyki agile sprawdzają się tylko w zespołach eksper-tów. Oczywiście należy tutaj rozróżnić praktyki inżynieryj-ne (głównie związane z Extreme Programming) od prak-tyk dotyczących stricte zarządzania projektem (Scrum). W tym pierwszym przypadku, weźmy praktykę TDD, oczywiście trzeba mieć wiedzę i doświadczenie w pisa-niu testów jednostkowych, ale tak jest przecież również w projektach, które nie używają „zwinnych” metod two-rzenia oprogramowania. Natomiast – myślę, że przyto-czona przez Pana opinia bardziej dotyczy pracy zespoło-wej, która jest „twardym rdzeniem” tych metod. Tutaj, Ze-społy muszą się wzajemnie komunikować, udzielać so-bie we właściwy sposób informacji zwrotnej, rozwiązywać konflikty, być kreatywnymi i szybko reagować na zmia-ny, których jest niemało. Oczywiście lepiej jest wdrażać Scrum w Zespołach, które zostały już, na skutek wcze-śniejszych doświadczeń projektowych, jakoś uformowa-ne. Niemniej jednak Scrum dostarcza zestaw wystarcza-jących narzędzi, które w bardzo szybki i „bezbolesny” sposób umożliwiają powstawanie „zwycięskich” zespo-łów projektowych, których niejednej firmie pozazdrościł-by dżentelmen Danny Ocean, bohater znanej wszystkim produkcji amerykańskiej. SDJ: Czy może podać Pan dane statystyczne, któ-

re potwierdzają, że wdrożenie CMMI albo praktyk Agile przyniosło wyraźną poprawę zysków firmy?

Zarówno danych, jak i pozytywnych przykładów jest tu-taj bardzo dużo. Oczywiście mamy pewien deficyt „na naszym podwórku”, ale mam nadzieję, że to się wkrót-ce zmieni. Jeśli chodzi o CMMI: redukcja o ok. 30% śred-nich kosztów naprawy błędów (Boeing); zwiększenie z 50% do 90% dokładności harmonogramu projektu (Ge-neral Motors); w ciągu 3 lat zwiększenie z 25% do 30% produktywności (Lockheed Martin, Harris, Siemens), zmniejszenie o 50% błędów w oprogramowaniu (Loc-kheed Martin) itd. Dodatkowo polecam wszystkim Czytel-nikom bardzo ciekawy artykuł o sukcesach związanych z wdrożeniem, jeszcze starej wersji, CMM for Software w Motoroli (konkretnie w oddziale Government Electro-nics Division), dostępny do pobrania na stronach IEEE (Diaz M., Sligo J., „How Software Process Improvement Helped Motorola”, IEEE Software, 14 (5), 1997.). Nato-miast w przypadku projektów, prowadzonych metodami agile, niestety danych statystycznych nie ma aż tak dużo. Co wynika z faktu, iż nie wszystkie firmy wdrażające no-we metody dokonują ich właściwej ewaluacji. Ale podam Panu kilka liczb, z wdrożeń, które prowadziłem w jednej z dużych firm telekomunikacyjnych w Polsce. Produktyw-ność w projektach po wdrożeniu, w porównaniu z wcze-śniejszymi baseline’ami, wzrosła w niektórych przypad-kach nawet dwukrotnie. Dodatkowo, na skutek niektórych praktyk XP (TDD, Automatyczna Regresja) gęstość błę-dów spadła prawie o 50%.

SDJ: Jakie wiąże pan plany w związku z firmą LOY-CON?MCH: Z Firmą LOYCON nawiązałem współpracę

w drugiej połowie 2009 roku, w związku z otwarciem jej nowego działu „LOYCON Process Solutions” (dotych-czas jej podstawową działalność stanowił outsourcing rozwoju oprogramowania i testów, z czym nadal sobie świetnie radzi), którego głównym zadaniem jest udzie-lanie wsparcia firmom informatycznym w zakresie po-prawy procesów wytwórczych w oparciu o model CMMI oraz metodę Scrum. Muszę powiedzieć, że wszystko roz-wija się naprawdę dobrym kierunku. Naszą niewątpliwą siłą jest Zespół. Dzięki prywatnym kontaktom zatrudnili-śmy wyjątkowych ekspertów głównie z zagranicy, posia-dających bogate doświadczenie w środowisku projektów międzynarodowych. W związku z tym na jesień przygoto-wujemy coś naprawdę wyjątkowego, proszę śledzić na-szą stronę www.loycon.pl i koniecznie www.tweeter.com/loycon, gdzie znajdziecie najbardziej porywające tweety o CMMI, Scrumie i tym wszystkim, co mnie i moim kole-gom spędza sen z oczu. SDJ: Brzmi świetnie. Ale jak Pan postrzega swoją dzia-

łalność w Polsce, gdzie konsultant ciągle postrzegany jest jako intruz w firmie?MCH: Niestety ma Pan trochę racji. Trochę sami je-

steśmy sobie winni. Oprócz dobrych referencji, nie ma żadnego mechanizmu weryfikacji firm konsultingowych, w zakresie jakości świadczonych usług. I nie łudźmy się, że taki mechanizm kiedykolwiek powstanie. Moim zda-niem problem bierze się stąd, że w Polsce jest sporo lu-dzi, którzy, mówiąc kolokwialnie, „liźnie trochę tematu”, kupi sobie bawełnianą koszulę, nawoskuje mokasyny, włoży średnio pasujący krawat i gotowe… idzie w świat głosić dobrą nowinę, że „jakość jest za darmo” (nawią-zanie do książki Philipa Crosby’ego o tym samym tytule). My chcemy to zmienić. SDJ: Życzę więc wielu sukcesów na tej drodze i bardzo

dziękuję za rozmowę!

MARIUSZ CHRAPKOJest wieloletnim praktykiem w zakresie doskonalenia proce-sów tworzenia oprogramowania w oparciu o model CMMI®. Wspiera �rmy informatyczne na terenie całej Europy, pro-wadząc coaching zespołów projektowych oraz szkolenia na różnych szczeblach organizacyjnych. Dodatkowo jest prak-tykiem we wdrażaniu i adaptacji metod Agile Software Deve-lopment (wcześniej Agile Coach/ Centrum Oprogramowania Motoroli w Krakowie), Programu Metryk Organizacyjnych, a także procesu Peer Review. Jest autorem wielu publikacji z zakresu inżynierii oprogramowania oraz prelegentem na konferencjach krajowych i międzynarodowych. Autor pierw-szej w Polsce książki na temat modelu CMMI raz jego prak-tycznego zastosowania. Od roku 2009 współpracuje z �rmą LOYCON® business solutions, udzielając wsparcia �rmom in-formatycznym w regionie Europy Centralnej i Wschodniej.

Page 67: SDJ 08 2010

KLUB PRO

Future ProcessingFuture Processing to dynamiczna firma technolo-giczna działająca na globalnym rynku oprogramo-wania. Jesteśmy zespołem wysokiej klasy specja-listów posiadających wiedzę i doświadczenie nie-zbędne do realizacji ambitnych projektów informa-tycznych. Jeśli programowanie to Twoja pasja do-łącz do nas! (możliwość pracy zdalnej).

http://www.future-processing.pl

Kei.plKei.pl działa na rynku usług hostingowych od 2000 roku. Do naszych zadowolonych Klientów z du-mą możemy zaliczyć wiele przedsiębiorstw sekto-ra MSP, instytucji oraz osób prywatnych. W ofer-cie Kei.pl znajdują się pakiety hostingowe, a także usługi dla wymagających Użytkowników – platfor-my e-Biznes oraz serwery fizyczne.

http://www.kei.pl

Opera SoftwareOpera Software’s vision is to deliver the best In-ternet experience on any device. We are offering browser for PC/desktops and embedded pro-ducts that operates across devices, platforms and operating systems. Our browser can deliver a faster, more stable and flexible Internet expe-rience than its competitors.

http://www.opera.com

Architektury systemów ITTwórca frameworków JUVE i serwera aplikacji AVAX oferuje usługi, doradztwo, rozwiązania do tworzenia nowoczesnych, dużych systemów i roz-wiązań informatycznych/internetowych, integrujące architektury ery post-J2EE/.NET, wykorzystujące MDD/MDA dla dziedzin – bankowość, telekomuni-kacja, handel, e-commerce, ERP/Workflow/CRM, rozwiązania internetowe, portalowe.www.mpsystem.com [email protected]

WSISiZ w WarszawieINFORMATYKA ZARZĄDZANIEstudia stopnia I i II (stacjonarne i niestacjonar-ne) specjalności: inżynierskie, magisterskie i licencjackie. Szczegółowe plany studiów, opi-sy poszczególnych specjalności – zapraszamy na stronę uczelni.

http://www.wit.edu.pl

TTS Company Sp. z o.o.Sprzedaż i dystrybucja oprogramowania komputero-wego. Import programów na zamówienie. Ponad 200 producentów w standardowej ofercie. Chcesz kupić oprogramowanie i nie możesz znaleźć polskiego do-stawcy? Skontaktuj się z nami – sprowadzimy nawet pojedyncze licencje.

http://www.OprogramowanieKomputerowe.pl

Softline rozwiązania mobilneWiodący producent systemów mobilnych, do-stawca aplikacji użytkowych dla biznesu (Sym-bian OS, Windows Mobile, J2ME ) zaprasza do współpracy. Zostań naszym partnerem. Dołącz do zespołu.

http://www.softline.com.pl

INFOTEX SP.J Śmietanowski i Wsp.Dystrybutor XP Unlimited – Serwer Terminali dla Windows XP i VISTA. Program umożliwia łącze-nie się z dowolnego klienta Windows, Linux z wy-korzystaniem protokołu RDP. Cena wersji Classic dla 5 użytkowników - 165€, dla nieograniczonej liczby - 235€. Ponadto oferujemy opiekę serwiso-wą i aplikacje internetowe na zamówienie.

http://www.infotex.com.pl

Proximetry Poland Sp. z o.o.Proximetry Poland Sp. z o.o. jest polskim od-działem amerykańskiej firmy Proximetry Inc. – dostawcy systemów zarządzania sieciami bez-przewodowymi opartymi na technologiach WiFi i WiMAX. Naszą misją jest dostarczenie klien-tom rozwiązań poprawiających jakość usług (QoS) dostarczanych drogą radiową. Dołącz do najlepszych i zostań członkiem naszej ekipy!

http://www.proximetry.com

Systemy bankowe, ISOF HEUTHES istnieje na rynku od 1989 r. Obok systemów informatycznych dla banków, ofe-ruje nowoczesne oprogramowanie do obsługi firm. System ISOF jest udostępniany klientom w trybie SaaS lub licencji. Pracuje na platfor-mie Linux i zawiera m.in. takie moduły jak CRM, DMS, Magazyn, Sprzedaż, Logistyka oraz Rachunkowość. http://www.isof.pl

Playsoft Playsoft jako lider portowania aplikacji na plat-formy mobilne wciąż powiększa bazę swo-ich klientów: EA Mobile, Sega, THQ, Kona-mi. W ramach rozszerzania swojej działalno-ści, poszukujemy doświadczonego programi-sty, który byłby odpowiedzialny za tworzenie aplikacji na platformy Iphone, Windows Mobi-le, Android.http://www.playsoft.fr

Volantis jest uznanym na całym świecie dostawcą rozwiązań mobil-nych dla firm udostępniających informacje oraz dane przez Internet (operatorów telefonii komórkowej, stacji tv, czasopism internetowych). Dzięki zasadzie „stwórz raz, uruchamiaj gdziekolwiek” nasze rozwią-zania zmniejszają złożoność, koszt i czas dostarczenia na rynek ser-wisów i aplikacji. Volantis jest członkiem organizacji W3C, a naszymi klientami są światowi potentaci telekomunikacyjni.


Recommended