Professional Documents
Culture Documents
Programista 1
Programista 1
Programista 1
Przed Wami pierwsze wydanie magazynu „Pro- pozwalające znacznie usprawnić proces tworze-
gramista”, które ukazało się również w postaci dru- nia aplikacji na urządzenia mobilne ze stajni Apple.
kowanej. Od niniejszego numeru magazyn staje się Znajdziecie u nas również ciekawy artykuł traktują-
miesięcznikiem. Przychyliliśmy się też do Waszych cy o popularnym ostatnio podejściu Domain Driven
próśb i przygotowaliśmy magazyn w wersji elektro- Design. Ponadto kontynuujemy tematykę poruszo-
nicznej w plikach ePub i .mobi oraz .pdf. ną w premierowym wydaniu naszego miesięcznika:
Rośnie ilość i złożoność otaczających nas syste- omawiamy możliwości nowego standardu języka
mów informatycznych. Rynek tabletów i smartfo- C++ oraz prezentujemy ciekawe tematy z zakresu
nów ciągle odnotowuje wzrosty i naturalną koleją inżynierii oprogramowania.
rzeczy powstają tysiące nowych aplikacji na te urzą- Na koniec pragniemy serdecznie podziękować
dzenia: od gier po programy użytkowe. Rozwijają za konstruktywne opinie dotyczące pierwszej, elek-
się języki programowania, powstają kolejne wersje tronicznej edycji magazynu. Wydanie to udostępni-
„mobilnych” systemów operacyjnych. W tej sytuacji liśmy bezpłatnie, by każdy mógł wyrobić sobie opi-
nie ma wyjścia: chcąc pozostać konkurencyjnym nię o projekcie. Olbrzymie zainteresowanie, jakim
w zawodzie Programisty, trzeba się na bieżąco roz- cieszył się premierowy egzemplarz, utwierdza nas
wijać, dokształcać. Naszym celem jest Wam to zada- w przekonaniu, że tego typu wydawnictwa brakowa-
nie ułatwić. ła na polskim rynku i dopinguje do jeszcze bardziej
W bieżącym numerze przedstawiamy bibliotekę wytężonej pracy nad magazynem.
Cocos2D: jeden z najpopularniejszych silników do Cieszy nas, że zdecydowana większość z Was po-
tworzenia gier na platformę iOS. Ponadto poleca- zytywnie oceniła inicjatywę! Dziękujemy za wsparcie
my praktyczny artykuł na temat języka Objective-C, i życzymy przyjemnej lektury!
w którym autor omawia przydatne jego właściwości,
Z wyrazami szacunku,
Redakcja
BIBLIOTEKI I NARZĘDZIA
Biblioteka Cocos2D: wprowadzenie............................................................................................. 6
Rafał Kocisz
JĘZYKI PROGRAMOWANIA
C++11 część I...................................................................................................................................... 22
Bartosz Szurgot, Mariusz Uchroński, Wojciech Waga
PROGRAMOWANIE GRAFIKI
Direct3D – podstawy........................................................................................................................ 50
Wojciech Sura
PROGRAMOWANIE URZĄDZEŃ
Wykorzystanie sensora Kinect w systemie Windows .............................................................. 56
Łukasz Górski
INŻYNIERIA OPROGRAMOWANIA
Domain Driven Design krok po kroku część II: Zaawansowane modelowanie DDD
– techniki strategiczne: konteksty i architektura zdarzeniowa............................................. 64
Sławomir Sobótka
KLUB LIDERA IT
Dokumentowanie architektury. Jak zorganizować proces rozwoju architektury?........... 72
Michał Bartyzel, Mariusz Sieraczkiewicz
WDROŻENIA
Projekt, oprogramowanie i wdrożenie platformy inwestycyjnej highsky.com.................. 76
Wojciech Holisz
KOMIKS........................................................................................................................................................ 49
Maciej Mazurek
O ile nie zaznaczono inaczej, wszelkie prawa do wszystkich materiałów zamieszczanych na łamach magazynu
Programista są zastrzeżone. Kopiowanie i rozpowszechnianie ich bez zezwolenia jest wzbronione. Naruszenie
praw autorskich może skutkować odpowiedzialnością prawną, określoną w szczególności w przepisach ustawy
o prawie autorskim i prawach pokrewnych, ustawy o zwalczaniu nieuczciwej konkurencji i przepisach kodeksu
cywilnego oraz przepisach prawa prasowego.
Redakcja magazynu Programista nie ponosi odpowiedzialności za szkody bezpośrednie i pośrednie, jak również
za inne straty i wydatki poniesione w związku z wykorzystaniem informacji prezentowanych na łamach magazy
nu Programista. Wszelkie nazwy i znaki towarowe lub firmowe występujące na łamach magazynu są zastrzeżone
przez odpowiednie firmy.
/ www.programistamag.pl / 5
BIBLIOTEKI I NARZĘDZIA
Rafał Kocisz
G
łównym celem niniejszego artykułu jest zapozna- wiście wspomagane akceleracją sprzętową). Jeśli jesteś
nie Czytelnika z podstawowymi blokami budulco- początkującym programistą gier, to zabawa z dwuwymia-
wymi, które oferuje biblioteka Cocos2D. Po jego rem jest wręcz zalecana (chociażby dlatego, że algorytmy
przeczytaniu będziesz wiedział, czego możesz spodziewać stosowane w tego typu grach są o wiele łatwiejsze w im-
się po tym silniku i na ile przydatny będzie on w Twoich plementacji w stosunku do ich odpowiedników stosowa-
projektach. Cocos2D to potężna biblioteka i szczegółowe nych w grach 3D).
jej opisanie to temat, który kwalifikuje się bardziej na Dodatkowo, za Cocosem stoi bardzo liczna, prężna
książkę niż na artykuł. Z tego względu niniejszy tytuł kła- i otwarta społeczność złożona w dużej mierze z niezależ-
dzie nacisk na ogólne zrozumienie koncepcji, na których nych programistów gier, co daje możliwość stosunkowo
opiera się biblioteka Cocos2D, oraz przedstawienie relacji łatwego uzyskania wsparcia. Ze względu na swoją popu-
między nimi. W jednym z ostatnich sekcji artykułu wska- larność Cocos2D może poszczycić się posiadaniem dużej
zane są materiały, z których zainteresowani Czytelnicy ilości wysokiej jakości materiałów edukacyjnych (samo-
będą mogli skorzystać w celu poszerzenia swojej wiedzy uczków, książek, forów dyskusyjnych) oraz narzędzi, któ-
na temat prezentowanej tu biblioteki. re wspierają i ułatwiają pracę z tą biblioteką.
Podsumowując, Cocos2D jest niewątpliwie biblioteką,
DLACZEGO COCOS2D? z którą warto się zapoznać. Jeśli masz ochotę zanurkować
w jego barwny świat, zapraszam do lektury dalszej części
Zanim przejdziemy do omówienia możliwości biblioteki niniejszego artykułu.
Cocos2D, spróbujmy odpowiedzieć sobie na podstawowe
pytanie: co sprawia, że warto zainteresować się właśnie GRAF SCENY
tym konkretnym rozwiązaniem?
Pierwszy ważny powód, dla którego warto rozważyć Najważniejsza, centralna koncepcja, wokół której zbudo-
używanie Cocos2D, to fakt, iż biblioteka ta jest całkowicie wana jest biblioteka Cocos2D to tzw. graf sceny (zwany
darmowa, zaś jej licencja pozwala tworzyć zarówno apli- czasami drzewem sceny bądź hierarchią sceny). Zrozu-
kacje komercyjne, jak i niekomercyjne. Licencja Cocos2D mienie tej koncepcji jest kluczowe w przypadku gdy ktoś
jest otwarta, silnik rozpowszechniany jest razem z kodem chce efektywnie korzystać z prezentowanego tu silnika.
źródłowym. Oznacza to, że nic nie stoi na przeszkodzie, Wyobraź sobie, że masz zaimplementować fragment
aby w razie potrzeby zajrzeć do kodu źródłowego biblio- graficznego interfejsu użytkownika w grze, coś podob-
teki czy wręcz wprowadzić do niej własne modyfikacje. nego do menu przedstawionego na Rysunku 1. Spróbuj
Cocos2D napisany jest w języku Objective-C. Biorąc spojrzeć na ten obraz okiem programisty i zastanów
pod uwagę, że Cocos2D obsługuje urządzenia z rodziny się, jak można by zorganizować model takiego interfej-
iOS oraz OS X, wybór ten wydaje się bardzo trafny: Ob- su użytkownika na poziomie kodu źródłowego. Pierwsza
jective-C to natywny język programowania w wymienio- myśl, która zapewne przyjdzie Ci do głowy, to przechowy-
nych systemach. Dla osób, które znają inny język obiekto- wanie płaskiej listy obiektów reprezentujących wszystkie
wy (np. C++, Java, C#), nauka Objective-C nie powinna widoczne elementy na ekranie (elementy tła, przyciski
sprawić większych trudności. Osobom rozpoczynającym i elementy tekstowe). Obiekty takie przechowywałyby in-
przygodę z programowaniem sugerowałbym poświęce- formacje o stanie poszczególnych elementów menu (np.
nie nieco czasu na solidne zapoznanie się z Objective-C, ich pozycja, stan, widoczność). Informacji tych można by
zanim na poważnie zaczną zajmować się programowanie użyć do rysowania całej sceny, obsługi zdarzeń użytkow-
gier pod iOS przy użyciu biblioteki Cocos2D. nika itp.
Jak sugeruje nazwa biblioteki, Cocos2D wspomaga pro- Podejście takie można by oczywiście z powodzeniem
gramowanie gier 2D. Warto podkreślić jednak, że mowa zastosować, ale... zanim zaczniesz kodować, zastanów
tutaj o nowoczesnych grach 2D, w których na porządku się, czy nie można by było zorganizować tego lepiej?
dziennym są wykonywane w czasie rzeczywistym trans- Powiedzmy, że chciałbyś zaimplementować prosty efekt
formacje obrazów (np. rotacja czy skalowanie), obsługa polegający na tym, że druga warstwa tła z umieszczony-
przeźroczystości czy post-processing (wszystko to oczy- mi na niej kontrolkami płynnie wsuwa się przy rozpoczę-
Rysunek 1. Proste menu gry (źródło: opracowanie własne) Rysunek 2. Efekt wsuwania się menu (źródło: opracowanie
własne)
ciu gry, zaś przy jej zakończeniu wysuwa się poza ekran
(patrz: Rysunek 2).
Sprawa niby prosta, jednakże przy zastosowaniu pła-
skiej listy jako struktury danych reprezentującej kolekcję
kontrolek, implementacja takiego efektu staje się nieco
uciążliwa: trzeba ręcznie wybrać wszystkie obiekty, które
chcemy wsuwać\wysuwać, i dla każdego z nich odpowied-
nio modyfikować ich pozycje. W tej sytuacji aż się prosi,
aby potraktować drugą warstwę tła jako płaszczyznę, na
której leżą wszystkie pozostałe kontrolki, i przesunąć ją,
razem ze wszystkim elementami, które są na niej umiesz-
czone. W tym celu możemy zastosować właśnie graf sceny.
Graf sceny to drzewiasta struktura danych pozwalająca
reprezentować obrazy (zarówno 2D i 3D) tak, aby zacho-
wać informację o hierarchii obiektów na nich występują-
cych. Kluczowym elementem grafu sceny jest węzeł (ang. Rysunek 3. Hierarchia sceny dla prostego menu w grze (źródło:
node), który spełnia dwojaką rolę: po pierwsze, repre- opracowanie własne)
zentuje wybrany element sceny; po drugie, jest kontene-
rem, który może przechowywać inne węzły, stanowiące Spróbujmy odnieść przedstawione wyżej rozważania
jego dzieci (ang. children). Węzeł posiadający dzieci na- do naszej przykładowej sceny. Na Rysunku 3 przedsta-
zywany jest rodzicem (ang. parent). W grafie sceny wy- wiona jest wspomniana scena z oznaczeniem elementów
stępuje jeden węzeł, który nie posiada rodzica; nazywa- hierarchii w grafie. Korzeniem grafu jest pierwsza war-
my go korzeniem (ang. root). Znamienne dla grafu sceny stwa tła (czerwony prostokąt otaczający). Korzeń ma
jest to, że każdy rodzic definiuje dla swoich dzieci swoisty jednego potomka: drugą warstwę tła (niebieski prostokąt
lokalny układ współrzędnych. Oznacza to, iż współrzęd- otaczający). Ten z kolei posiada czwórkę dzieci: przyci-
ne dzieci (a także inne ich właściwości) rozpatrywane są ski (żółte prostokąty otaczające). Każdy przycisk posia-
w odniesieniu do układu rodzica. Np. jeśli węzeł rodzic da jednego potomka, którym jest umieszczony pod nim
(korzeń) ma pozycję (50, 50), zaś jego potomek znaj- napis. Do reprezentacji takiej sceny potrzebowalibyśmy
duje się w punkcie (10, 15), to rzeczywista (ekranowa) dwóch konkretnych klas-węzłów reprezentujących sta-
pozycja tego drugiego wyniesie (60, 75). W takim ujęciu, tyczny obrazek (elementy tła i przyciski) oraz tekst (napi-
gdy zmienimy pozycję danego węzła, to automatycznie sy pod przyciskami).
przemieszczone zostaną jego dzieci. Mając do dyspozycji tak zorganizowaną scenę, za-
Każdy węzeł w grafie sceny posiada identyczny inter- programowanie efektu wsuwania się drugiej warstwy tła
fejs, opisany zazwyczaj przez abstrakcyjną klasę bazową. z umieszczonymi na niej kontrolkami staje się bardzo pro-
Po tej klasie dziedziczą inne klasy, reprezentujące kon- ste; wystarczy jedynie odpowiednio zmodyfikować pozy-
kretne węzły. Czytelnicy, którzy mieli styczność z tzw. cję węzła reprezentującego tę warstwę, a o właściwe po-
wzorcami projektowymi (ang. design patterns), słusznie zycjonowanie pozostałych elementów zadba graf sceny.
skojarzą przedstawiony tutaj opis ze wzorcem kompozyt Graf sceny to nieocenione narzędzie przy tworzeniu
(ang. composite); de facto graf sceny jest niemalże wzor- aplikacji wyświetlających złożone obrazy zbudowane
cowym przykładem takiego podejścia projektowego. z grup powiązanych ze sobą obiektów (pod tę kategorię
/ www.programistamag.pl / 7
BIBLIOTEKI I NARZĘDZIA
można z powodzeniem podciągnąć większość nowocze- wyglądać nieco inaczej od tej, która jest tutaj przedsta-
snych gier 2D oraz 3D). Jest on również bardzo przydat- wiona (dotyczy ona Cocos2D w wersji 1.0.1), jednak-
ny w innych zastosowaniach, np. określanie widoczności że szereg koncepcji reprezentowanych przez wybrane
obiektów czy detekcja kolizji. Na tym etapie ważne jest, klasy niewątpliwie pozostaną niezmienne. Klasy te będą
abyś zrozumiał podstawową ideę tego wzorca projekto- szczegółowo omówione w kolejnych podpunktach niniej-
wego, gdyż stanowi on serce biblioteki Cocos2D. szego artykułu.
Zanim jednak do tego przejdziemy, przyjrzyjmy się
KLASA BAZOWA CCNODE bardziej szczegółowo interfejsowi klasy CCNode. Jak już
wcześniej wspominałem, jest to klasa abstrakcyjna i nie
Tak jak wspominałem w poprzednim punkcie, kluczo- ma bezpośredniej reprezentacji wizualnej, jednakże pełni
wymi elementami w grafie sceny są węzły. Każdy wę- bardzo istotną rolę, gdyż zawiera pola i metody wspólne
zeł dziedziczy po klasie bazowej, która definiuje spój- dla wszystkich węzłów przechowywanych w grafie sceny.
ny interfejs dla wszystkich obiektów umieszczanych Oto lista najważniejszych z nich:
w hierarchii. W przypadku biblioteki Cocos2D klasa
ta nazywa się CCNode. Po tej klasie dziedziczą kolejne ■■ tworzenie nowego węzła:
klasy, reprezentujące konkretne obiekty, które można
umieszczać w grafie sceny. Na Rysunku 4 przedstawio- CCNode* childNode = [CCNode node];
Jak łatwo się można domyśleć, parametr z przekazywany CCAction* retrievedAction = [node
getActionByTag:37];
w metodzie addChild określa głębokość (ang. depth) wę-
zła w scenie i pozwala kontrolować kolejność rysowania
elementów przechowywanych w grafie. Zasada jest pro- ■■ zastopowanie akcji określonej za pomocą identyfikatora:
sta: elementy z małymi wartościami z rysowane są jako
pierwsze, zaś te, które mają największą głębokość – jako [node stopActionByTag:37];
/ www.programistamag.pl / 9
BIBLIOTEKI I NARZĘDZIA
Listing 1. Harmonogramowanie prostej funkcji obsługującej procesu uaktualniania stanu gry i w rezultacie mają bar-
logikę węzła dzo istotny wpływ na wydajność aplikacji:
-(void) scheduleUpdateMethod
{ ■■ kCCDirectorTypeDisplayLink: gwarantuje najlepszą
[self scheduleUpdate]; wydajność oraz płynność renderowania, dzięki zasto-
} sowaniu mechanizmu synchronizacji procesu uak-
tualniania ekranu z jego sprzętowym odświeżeniem;
-(void) update:(ccTime)delta
{ niestety – mechanizm ten dostępny jest od wersji 3.1
// Ta metoda będzie wywoływana po narysowaniu systemu iOS wzwyż,
// kolejnej ramki gry. ■■ kCCDirectorTypeNSTimer: gwarantuje największą
}
przenośność (będzie działać w każdej wersji systemu
iOS), ale jest za to najwolniejszy,
REŻYSER, SCENA, WARSTWA ■■ kCCDirectorTypeThreadMainLoop: szybki, ale spra-
wia problemy w sytuacji, kiedy chcemy używać z po-
Chciałbym przedstawić teraz trzy klasy, które pełnią w bi- ziomu aplikacji Cocos2D widoków UIKit,
bliotece Cocos2D bardzo ważne role. Mowa tu o klasach ■■ KCCDirectorTypeMainLoop: jak wyżej.
CCDirector, CCScene oraz CCLayer. Dwie ostatnie z wy-
mienionych dziedziczą z CCNode i podobnie jak ona, nie mają
wizualnej reprezentacji. CCScene to kontener dla wszyst- Domyślnie biblioteka Cocos2D korzysta z reżysera typu
kich obiektów znajdujących się na scenie. Obiekty tej klasy kCCDirectorTypeDisplayLink, zaś w sytuacji gdy nie jest
reprezentują zazwyczaj ekrany gry: menu główne, tabelę on dostępny (na dzień dzisiejszy bardzo mało prawdopodob-
wyników czy wreszcie – właściwą rozgrywkę. Z kolei klasa na sytuacja), przełącza się na typ kCCDirectorTypeNSTimer.
CCLayer (warstwa) służy do grupowania obiektów, głównie Jeśli chciałbyś sam określić typ reżysera, to musisz zmo-
w celu zapewnienia właściwej kolejności ich renderowania. dyfikować następujący fragment kodu źródłowego w klasie
Można sobie np. wyobrazić, że ekran rozgrywki składa się reprezentującej delegata aplikacji:
z trzech warstw: statycznego tła, części dynamicznej (ru-
chome obiekty gry) oraz paska statusu przedstawiające- if ( ! [CCDirector
setDirectorType:
go liczbę zdobytych punktów oraz żyć. Ważną cechą klasy kCCDirectorTypeDisplayLink] )
CCLayer jest to, że potrafi ona przechwytywać zdarzenia [CCDirector
generowane przez użytkownika za pośrednictwem takich setDirectorType:
kCCDirectorTypeDefault];
kontrolerów jak ekran dotykowy czy akcelerometr. Szcze-
gólną rolę w tej układance pełni klasa CCDirector; pełni
ona rolę reżysera – decyduje o tym, która scena (CCScene) Kolejną klasą z inwentarza biblioteki Cocos2D, z którą
będzie w danym momencie aktywna. warto się szczegółowo zapoznać, jest CCScene. Obiekty
Na początek przyjrzyjmy się, co oferuje klasa CCDirec- tej klasy są zawsze korzeniami w grafie sceny. Rozwią-
tor. Po pierwsze, klasa ta jest tzw. singletonem (singleton zanie to wydaje się momentami odrobinę sztuczne (klasa
jest to wzorzec projektowy, który zapewnia, że dana klasa CCScene w zasadzie nie rozszerza w żaden sposób funk-
posiada tylko jedną instancję). Fakt ten wydaje się być cjonalności CCNode), jednakże metody runWithScene,
dość oczywisty (czy widziałeś kiedyś film lub przedsta- replaceScene czy pushScene z klasy CCDirector potra-
wienie, za które odpowiadało dwóch reżyserów?). Głów- fią współpracować tylko z obiektami tego typu. Poza tym,
ne zadanie reżysera w bibliotece Cocos2D to zarządzanie sceny można opakować obiektami klas dziedziczących po
scenami oraz przechowywanie danych konfiguracyjnych. klasie bazowej CCSceneTransition, co pozwala uzyskać
W szczególności klasa CCDirector odpowiada za: miłe dla oka efekty przejść między scenami gry.
Pracując z biblioteką Cocos2D, zazwyczaj przyjmuje
■■ startowanie sceny, się konwencję, że dziećmi sceny są jedynie warstwy (tj.
■■ zamianę bieżącej sceny, obiekty wywodzące się z klasy CCLayer), zaś one dopiero
■■ wkładanie (ang. pushing) nowej sceny na bieżącą, zawierają w sobie węzły reprezentujące konkretne obiek-
■■ zdejmowanie (ang. popping) bieżącej sceny, ty występujące w grze.
■■ gwarantowanie dostępu do bieżącej sceny, Bardzo przydatnym mechanizmem jest wkładanie (ang.
■■ pauzowanie, kontynuowanie oraz kończenie gry, pushing) oraz zdejmowanie (ang. popping) bieżącej sceny
■■ przechowywanie i gwarantowanie dostępu do global- z poziomu reżysera. Operacje te niewątpliwie kojarzą się
nych danych konfiguracyjnych biblioteki Cocos2D, ze strukturą danych zwaną stosem. I rzeczywiście, myśląc
■■ gwarantowanie dostępu do okna oraz widoku OpenGL, o tym, w jaki sposób klasa CCDirector zarządza scenami,
■■ konwertowanie współrzędnych UIKit i OpenGL, można posłużyć się modelem stosu. Oczywiście, wkładając
■■ kontrolowanie procesu uaktualniania stanu gry. nową scenę na stos, bieżąca nadal pozostaje widoczna.
Mechanizm ten jest bardzo przydatny np. w sytuacji kie-
Pracując z biblioteką Cocos2D, mamy do wyboru cztery dy w trakcie gry chcemy wyświetlić okno dialogowe, bądź
typy reżyserów. Typy te implikują sposób kontrolowania podręczne menu. W tej sytuacji wkładamy nową scenę
/ www.programistamag.pl / 11
BIBLIOTEKI I NARZĘDZIA
Kiedy tylko do właściwości isTouchEnabled zostanie W tym momencie warto napisać kilka
przypisana wartość YES, Cocos2D zacznie wywoływać na słów o tym, w jaki sposób Cocos2D po-
obiekcie reprezentującym warstwę cały szereg funkcji zycjonuje duszki. Tutaj mała zagadka:
zwrotnych służących do obsługi zdarzeń generowanych jak według Ciebie będzie wyglądał ekran
przez ekran dotykowy: po narysowaniu duszka załadowanego
za pomocą przedstawionego wyżej frag-
■■ -(void) ccTouchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event – ta funkcja jest wy- Rysunek 6. Przykładowa tekstura używana do wyświetlenia
woływana, kiedy palec dotknie ekranu dotykowego, duszka (źródło: http://www.lostgarden.com)
/ www.programistamag.pl / 13
BIBLIOTEKI I NARZĘDZIA
teksturę; rysuj korzystając z danych zawartych w aktualnie Lekarstwem na opisane wyżej problemy jest stosowa-
załadowanej teksturze; załaduj kolejną teksturę; itd. Ha- nie tzw. atlasów tekstur (ang. texture atlases). Są to po
czyk w całej tej zabawie polega na tym, że operacja przeła- prostu obrazki o wymiarach kompatybilnych z wymia-
dowania tekstury jest bardzo czasochłonna. Tak bardzo, że rami tekstur, do których upakowane są wszystkie ele-
niefrasobliwe jej używanie może literalnie zabić wydajność menty graficzne wykorzystywane w grze. Oprócz tego,
gry. Wyobraź sobie, że tworzysz dwuwymiarową grę typu w oddzielnym pliku umieszcza się informacje o położeniu
shoot'em up, czyli klasyczną strzelaninę, w której Twój bo- oryginalnych obrazków na atlasie, dzięki czemu można
hater pędząc w uzbrojonym pojeździe kosmicznym niszczy się do nich odwoływać w trakcie działania gry. Na Ry-
hordy Obcych próbujących podbić Ziemię. Gra oczywiście sunku 8 pokazany jest przykładowy atlas tekstur. Pro-
składa się z całego szeregu animacji opartych na duszkach. ces tworzenia takich atlasów jest zazwyczaj zautoma-
Pół biedy, jeśli wpadniesz na pomysł, aby klatki animacji tyzowany. Istnieją dedykowane narzędzia, korzystające
pojazdu gracza i poszczególnych przeciwników umieścić w zaawansowanych algorytmów optymalizacyjnych słu-
w oddzielnych plikach. Gorzej, jeśli każdą klatkę zechcesz żące po to aby składać dużą liczbę małych plików gra-
trzymać w osobnym obrazku. Jeśli tak zrobisz, gwarantuję ficznych w jeden duży. Część z tych narzędzi przysto-
Ci, że Twoja gra będzie (może) działać w granicach 2-3 sowana jest do współpracy z biblioteką Cocos2D. Jedną
FPS'ów, nawet na urządzeniach nowej generacji. z ciekawszych opcji w tym zakresie jest TexturePacker
(http://www.texturepacker.com/), który w podstawowej rować „w locie”, bez posiłkowania się oddzielną teksturą.
wersji dostępny jest za darmo. Z tej racji, obiektów klasy CCLabelTTF w praktyce używa
Cocos2D oczywiście oferuje wsparcie dla atla- się głównie do wyświetlania informacji użytecznych przy
sów tekstur, które obsługiwane są za pomocą klas debugowaniu aplikacji czy do szybkiego prototypownia.
CCSpriteFrameCache oraz CCSpriteFrame. Przedstawie- Cocos2D oczywiście wspiera czcionki bitmapowe. Do
nie procesu tworzenia atlasu i obsługiwania go z poziomu ich obsługi wykorzystuje się klasę CCLabelBMFont. Ko-
silnika wykracza poza ramy niniejszego artykułu. Ważne rzystanie z tej klasy jest równie proste jak korzystanie
jest, abyś był świadom istnienia tych klas, aczkolwiek jeśli z CCLabelTTF (patrz Listing 5), ale... Analizując ten Li-
planujesz pracować z Cocosem na poważnie, to prędzej czy sting, można zauważyć dwie istotne kwestie. Po pierwsze,
później będziesz musiał zapoznać się z tym zagadnieniem. aby wczytać pożądany font, musimy wskazać konkretny
W razie czego ciekawy samouczek opisujący ten temat mo- plik .fnt. Od razu pojawia się pytanie: skąd go wziąć? Po
żesz znaleźć tutaj: http://www.raywenderlich.com/1271/ drugie, przy wczytywaniu fonta nie podajemy jego wiel-
how-to-use-animations-and-sprite-sheets-in-cocos2d. kości, co wydaje się zrozumiałe: przecież wymiar fonta
bitmapowego zależy od tego, jak go sobie narysujemy. Te
RENDEROWANIE TEKSTU dwa spostrzeżenia wiążą się z podstawowymi problemami
dotyczącymi stosowania fontów bitmapowych. Pierwsza
Renderowanie napisów jest jednym z nieodzownych ele- kwestia w zasadzie nie jest problemem sensu stricto. To
mentów każdej niemalże gry. W tej sytuacji nie do pomy- raczej konsekwencja decyzji używania takiego a nie inne-
ślenia jest, aby Cocos2D nie oferował żadnych usprawnień go formatu fonta. Po prostu w celu stworzenia pliku .fnt
w tym zakresie. Jak się zapewne domyślasz, uspraw- (a także towarzyszącego mu pliku .png) musimy skorzy-
nienia takie istnieją i zamierzam je w tym podpunkcie stać z narzędzia. Opcji jest kilka:
zaprezentować.
Najprostszym sposobem wyświetlenia tekstu przy ■■ Hiero (http://slick.cokeandcode.com/demos/hiero.jnlp):
pomocy biblioteki Cocos2D jest użycie obiektu klasy aplikacja webowa napisana w Javie; jej główną zaletą
CCLabelTTF. Klasa ta, jak się łatwo domyśleć po jej na- jest fakt, iż jest ona darmowa; niestety, poza tym stwa-
zwie, służy do wyświetlania napisów w oparciu o czcionki rza sporo problemów związanych z błędami istniejącymi
TrueType. Podstawowe użycie tej klasy pokazane jest na w tym narzędziu,
Listingu 4. ■■ Glyph Designer (http://glyphdesigner.71squared.com/):
natywna aplikacja przeznaczona dla systemów z ro-
Listing 4. Proste użycie klasy CCLabelTTF dziny OS X; wysoce stabilna i oferująca szereg przy-
datnych funkcji, aczkolwiek płatna (aktualna cena:
CCLabelTTF* label = 29.99$); jeśli planujesz na poważnie zająć się progra-
[CCLabelTTF labelWithString:@"Hello, Cocos2D!"
fontName:@"Arial" mowaniem gier pod iOS z wykorzystaniem Cocos'a, to
fontSize:32]; bezwzględnie polecam zakup tego narzędzia,
■■ BMFont (www.angelcode.com/products/bmfont/): na-
Jak widać, rzecz jest bardzo prosta. Inna sprawa, że pro- tywna aplikacja przeznaczona pod systemy z rodziny...
sto nie zawsze oznacza wydajnie. Zanim zaczniesz uży- Windows. Darmowa i stabilna, jednakże – jeśli Twoim
wać klasy CCLabelTTF, musisz być świadom, że pod ma- podstawowym środowiskiem pracy jest OS X – to jej
ską działa ona w ten sposób, iż w locie generuje teksturę używanie może być nieco kłoptliwe.
i renderuje do niej żądany tekst, korzystając z odpowied-
niej definicji czcionki TrueType. Takie renderowanie do Przedstawienie mechanizmu generowania fonta bitmapo-
pośredniej tekstury jest operacją dość zasobożerną i jeśli wego przekracza ramy niniejszego artykułu; zaintereso-
planujesz na bieżąco modyfikować zawartość tekstu re- wanych odsyłam do dokumentacji wymienionych wyżej
prezentowanego przez CCLabelTTF to licz się z potencjal- narzędzi.
nym spadkiem wydajności Twojej aplikacji. Drugi problem związanym z używaniem fontów bitma-
Drugi problem związany z używaniem czcionek powych to stały rozmiar czcionki. Oczywiście węzły typu
TrueType jest tego rodzaju, że czasami trudno jest dobrać CCLabelBMFont można normalnie skalować (jak każdy
krój liter odpowiedni do Twojej gry. W praktyce często inny węzeł w grafie sceny), jednakże w takim przypadku
robi się tak, że grafik przygotowuje dedykowany zestaw traci się jakość (co akurat w przypadku fontów jest dość
znaków dla danej gry w postaci tzw. czcionki bitmapowej istotne). Rozwiązaniem jest przygotowanie fontów bitma-
(ang. bitmap font). Czcionka taka zazwyczaj dostarczana powych w różnych wielkościach.
jest w postaci dwóch plików: tekstury, na której umiesz-
czone są poszczególne znaki, oraz metadanych (np. pliku Listing 5. Proste użycie klasy CCLabelBMFont
w formacie XML, JSON czy plist), które definiują pozycje
i atrybuty poszczególnych znaków na teksturze. Prakty- CCLabelBMFont* label =
[CCLabelBMFont
ka pokazuje, że czcionki bitmapowe są o wiele częściej labelWithString:@"Hello, Cocos2D!"
używane w grach niż czcionki TrueType. Napis oparty na fntFile:@"bitmapfont.fnt"];
czcionce bitmapowej można też łatwiej i wydajniej rende-
/ www.programistamag.pl / 15
BIBLIOTEKI I NARZĘDZIA
SYSTEMY CZĄSTECZEK nic a nic nie obchodzi, czy działa na prostym węźle typu
CCSprite, czy na złożonej scenie. Dzięki kompozytowej
Nieodłącznym elementem nowoczesnych gier 2D są strukturze węzłów obracanie pojedynczego duszka czy
efekty oparte na systemach cząsteczek. Pisząc w dużym warstwy reprezentującej złożoną grupę obiektów odbywa
uproszczeniu, system taki złożony jest ze zbioru wielu się w ten sam sposób. Akcje są kluczowym elementem
cząstek emitowanych przez źródło. Każda cząstka ma biblioteki Cocos2D; można wręcz pokusić się o stwierdze-
określone parametry, między innymi: czas życia, rozmiar, nie, że obok koncepcji grafu sceny stanowią jeden z trzo-
kolor, położenie czy prędkość. Kluczowym elementem nów filozofii na której opiera się ten silnik. W niniejszym
systemu jest tzw. emiter, którego głównym zadaniem jest podpunkcie zapoznamy się z nimi bardziej szczegółowo.
tworzenie cząstek, sterowanie ich ruchem i modyfikacja Klasą bazową dla wszystkich akcji jest CCAction. Jest
ich parametrów. Po upływie czasu życia cząstki są usu- to klasa abstrakcyjna, z której dziedziczą cztery inne
wane. Za pomocą systemów cząsteczek można wizualizo- klasy:
wać szereg ciekawych efektów, np.: deszcz, śnieg, dym,
ogień, wybuchy, opary itp. ■■ CCFiniteTimeAction: klasa abstrakcyjna reprezen-
Cocos2D oferuje bazową klasę abstrakcyjną CCParti- tująca kategorię akcji wykonywalnych w skończonym
cleSystem oraz dwie klasy konkretne: CCParticleSys- czasie; z niej z kolei wywodzą się dwie kolejne kate-
temPoint oraz CCParticleSystemQuad. Klasy te repre- gorie akcji: CCActionInstant oraz CCActionInterval
zentują uniwersalny system cząsteczkowy zdefiniowany (więcej szczegółów na ich temat podam za chwilę),
przez szereg właściwości definiujące różnorakie aspekty ■■ CCFollow: akcja, która pozwala, aby określony węzeł
jego działania. W celu stworzenia własnego systemu czą- podążał za innym węzłem,
steczek musimy stworzyć klasę dziedziczącą CCParticle- ■■ CCRepeatForever: akcja, która powtarza inną akcję
SystemPoint bądź CCParticleSystemQuad, a następnie w nieskończoność,
odpowiednio skonfigurować odziedziczone właściwości ■■ CCSpeed: akcja, która zmienia częstotliwość wywoły-
(różnice pomiędzy tymi klasami mają charakter czysto im- wania metody uaktualniającej logikę innej akcji.
plementacyjny; pierwsza z nich jest zoptymalizowana pod
starsze urządzenia z rodziny iOS, druga – pod nowsze). Na Listingu 6 przedstawiony jest fragment kodu, który
Jak można się domyśleć, takie „ręczne” dostrajanie syste- tworzy akcję obracającą w nieskończoność węzeł, do któ-
mu z poziomu kodu źródłowego nie jest łatwym zadaniem. rego będzie przypisana.
Na szczęście istnieje druga, łatwiejsza droga, polegają-
ca na skorzystaniu z narzędzia Particle Designer (http:// Listing 6. Definicja akcji obracającej w nieskończoność wę-
particledesigner.71squared.com/). Narzędzie to pozwala zeł, do którego będzie przypisana
za pomocą szeregu kontrolek graficznego interfejsu użyt-
// Stwórz akcję, która wykonuje pełny obrót węzła
kownika dostroić system cząsteczek, przy czym w okienku // (360 stopni).
podglądu możemy w czasie rzeczywistym oglądać rezulta- CCRotateBy* rotateBy360 =
ty wprowadzanych przez nas zmian. Kiedy uzyskamy już [CCRotateBy actionWithDuration:2 angle:360];
zadowalający nas efekt, wystarczy wyeksportować konfi-
// Stwórz akcję, która powtarza w nieskończoność
gurację systemu w formacie Cocosa (plik .plist) i stworzyć // akcję rotateBy360.
obiekt CCParticleSystemQuad w następujący sposób: CCRepeatForever* rotateForever =
[CCRepeatForever actionWithAction:rotateBy360];
CCParticleSystem* system =
// Przypisz akcję do węzła.
[CCParticleSystemQuad
particleWithFile:@"ps.plist"]; [spriteNode runAction:rotateForever];
po czy możemy cieszyć się pięknym efektem cząsteczko- Większość akcji, z którymi mamy do czynienia, dzie-
wym w naszej grze. je się w określonym przedziale czasu. Wszystkie ta-
Particle Designer jest narzędziem płatnym (aktualna kie akcje reprezentowane są przez abstrakcyjną klasę
cena: 7.99$), jednakże warto się w niego zaopatrzyć, CCActionInterval. Najbardziej podstawową z tych akcji
szczególnie jeśli planujesz używać w swojej grze dużo jest niewątpliwie CCMoveTo. Akcja ta odpowiada za prze-
efektów opartych na cząsteczkach. sunięcie węzła do określonej pozycji w zadanym interwale
czasu. Na przykład tak skonfigurowana akcja:
AKCJE
CCMoveTo* moveTo =
[CCMoveTo actionWithDuration:1
Obiekty klas wywodzących się z CCAction, o których position:CGPointMake(100, 100)];
opowiadałem bardzo ogólnie na początku niniejszego ar-
tykułu, reprezentują akcje, które można wykonywać na
węzłach. Za ich pomocą można przesuwać, skalować, ob- spowoduje, że węzeł, do którego będzie ona przypisana
racać i wykonywać wiele innych ciekawych operacji na przesunie się do punktu (100, 100) w przeciągu jednej
elementach grafu sceny. Obiektu reprezentującego akcję sekundy. Zamienne dla akcji wywodzących się z klasy ba-
CCMoveTo* moveToP2 =
Mam nadzieję, że w tym momencie zaczynasz już zdawać
[CCMoveTo actionWithDuration:1
position:CGPointMake(100, 50)]; sobie sprawę, jak potężnym, użytecznym, a jednocześnie
łatwym w obsłudze mechanizmem są akcje. Ale to wcale
CCMoveTo* moveToP3 = nie koniec ich możliwości!
[CCMoveTo actionWithDuration:1
Inna, ciekawa kategoria akcji to akcje wywodzące się
position:CGPointMake(100, 100)];
z klasy bazowej CCActionEase. Są to tzw. akcje łagodzące
CCMoveTo* moveToP4 = (ang. easing actions). Mówiąc ogólnikowo, ich zadaniem
[CCMoveTo actionWithDuration:1 jest modyfikacja danego efektu w czasie. Jak to wygląda
position:CGPointMake(50, 100)];
w praktyce? Wyobraź sobie, że przesuwasz duszka za po-
/ www.programistamag.pl / 17
BIBLIOTEKI I NARZĘDZIA
mocą akcji CCMoveTo. Zauważ, że ruch duszka jest rów- Listing 8. Modyfikacja charakterystyki ruchu węzła za pomo-
nomierny. Korzystając z akcji łagodzących, możesz wpły- cą akcji łagodzącej
wać na charakterystykę tego ruchu, np. możesz sprawić,
CCMoveTo* moveTo =
że na początku i na końcu ruchu duszek będzie poruszał [CCMoveTo actionWithDuration:5
się wolniej, tak aby sprawić wrażenie, że powoli nabiera position:CGPointMake(200, 200)];
prędkości ruszając z miejsca, a później wytraca ją, ha-
CCEaseInOut* ease =
mując. Korzystając z tego narzędzia, możesz sprawić, że
[CCEaseInOut
Twoje animacje będą bardziej dynamiczne i wygładzone. actionWithAction:move
Na Listingu 8 pokazane jest, w jaki sposób można użyć rate:4];
akcji łagodzącej w celu zmodyfikowania charakterystyki
[sprite runAction:ease];
ruchu węzła.
Zastosowanie akcji typu CCEaseInOut sprawi, że węzeł Listing 9. Ruch węzła po kwadracie zrealizowany za pomocą
będzie na początku ruchu powoli przyśpieszał, zaś przy akcji CCSequence, z wywoływaniem funkcji zwrotnej
jego końcu – łagodnie zwalniał. Oczywiście Cocos2D po-
CCMoveTo* moveToP2 =
siada w swoim arsenale cały szereg klas reprezentujących [CCMoveTo actionWithDuration:1
inne funkcje łagodzące. Hierarchia tych klas przedstawio- position:CGPointMake(100, 50)];
na jest na Rysunku 9. Szczegółowe informacje na temat
CCMoveTo* moveToP3 =
ich działania znajdują się w dokumentacji biblioteki:
[CCMoveTo actionWithDuration:1
http://www.cocos2d-iphone.org/wiki/doku.php/ position:CGPointMake(100, 100)];
prog_guide:actions_ease.
CCMoveTo* moveToP4 =
Kolejną kategorią akcji oferowaną przez prezentowany
[CCMoveTo actionWithDuration:1
silnik są tzw. akcje bazujące na siatce (ang. grid actions). position:CGPointMake(50, 100)];
Głównym zadaniem tych akcji jest dostarczenie miłych
dla oka efektów wizualnych o quasi-trójwymiarowym CCMoveTo* moveToP1 =
[CCMoveTo actionWithDuration:1
charakterze. Wszystkie akcje tego typu dziedziczą po kla- position:CGPointMake(50, 50)];
sie bazowej CCGridAction. Hierarchia tych klas pokaza-
na jest na Rysunku 10. Efekty te mają charakter pseudo CCCallFunc* callback =
[CCCallFunc actionWithTarget:self
3D, w ramach przykładu można tu wymienić efekt prze-
selector:@selector(onSequenceFinished)];
wrócenia kratki (CCPageTurn3D), efekt fali (CCWaves) czy
efekt płynu (CCLiquid). To, na co warto zwrócić uwagę, CCSequence* sequence =
[CCSequence actions:moveToP2,
to fakt, że efekty oparte na siatce będą działać jedynie
moveToP3,
wtedy, gdy nasza aplikacja będzie miała dostęp do bufora moveToP4,
głębokości (ang. depth buffer). W celu tego zapewnienia moveToP1,
należy ręcznie zmodyfikować kod odpowiadający za ini- nil];
/ www.programistamag.pl / 19
BIBLIOTEKI I NARZĘDZIA
■■ integracja z sinikami symulacji fizycznych (Box2D rzenia pełnej gry; zawiera również spory zastrzyk wiedzy
i Chipmunk), na temat bibliotek fizycznych Box2D oraz Chipmunk,
■■ kompleksowe narzędzia wspomagające pracę z Co- ■■ Learn Cocos2D Game Development With iOS 5, Apress,
cos2D, np. CocosBuilder (http://cocosbuilder.com/), 2011; książka autorstwa Steffena Itterheima oraz An-
LevelHelper (http://www.levelhelper.org/) czy Sprite- dreasa Löwa; kolejne świetne wprowadzenie w temat
Helper (http://www.spritehelper.org/). Cocosa; dużo informacji na temat narzędzi wspoma-
gających (między innymi wymienionych w niniejszym
Informacje na temat wymienionych tu (a także wielu in- artykule),
nych tematów) związanych z biblioteką Cocos2D znaleźć ■■ Cocos2d for iPhone 1 Game Development Cookbook,
możesz między innymi w materiałach, które przedstawi- Packt Publishing, 2011; książka autorstwa Nathana
łem w przedostatnim podpunkcie tego tekstu. Burby zorganizowana jako zestaw praktycznych po-
rad i rozwiązań zadań związanych z programowaniem
CO DALEJ? gier w oparciu o silnik Cocos2D; mocną stroną tej po-
zycji jest fakt, iż zahacza ona również o zagadnienia
Nasza wspólna podróż po świecie Cocosa powoli się koń- zaawansowane,
czy. Mam nadzieję, że koniec niniejszego artykułu bę- ■■ http://cocoadevcentral.com/d/learn_objectivec/: bar-
dzie dopiero początkiem Twojej przygody z biblioteką dzo przystępny (a zarazem krótki) samouczek pre-
Cocos2D! Pamiętasz, jak we wstępnie pisałem, że jed- zentujący najważniejsze aspekty języka Objective-C;
ną z silnych stron tego rozwiązania jest dostęp do dużej jeśli nie miałeś wcześniej do czynienia z tym językiem,
ilości wysokiej jakości materiałów edukacyjnych? Jeśli a masz już doświadczenia z innymi językami obiekto-
po przestudiowaniu niniejszego tekstu czujesz niedosyt wymi, zacznij naukę od przeczytania tego dokumentu,
wiedzy (efekt jak najbardziej pożądany!), to na koniec ■■ Programming in Objective-C, 4th Edition, Addison-We-
chciałbym wskazać Ci kilka ciekawych miejsc w sieci oraz sley, 2011: książka autorstwa Stephena G. Kochana
książek, które pomogą Ci lepiej zapoznać się z Cocosem: traktująca o języku Objective-C; jeśli zajdzie potrzeba,
abyś poznał tajniki natywnego języka systemu iOS, ta
■■ http://www.cocos2d-iphone.org: strona domowa bi- książka będzie świetnym przewodnikiem.
blioteki; tutaj warto na bieżąco sprawdzać, co się dzieje
w świecie Cocosa; zajdziesz tu najważniejsze nowinki Miej świadomość, że powyższa lista przedstawia moje ulu-
na temat biblioteki oraz szereg innych ciekawych infor- bione materiały edukacyjne związane z Cocosem i Objec-
macji (szczególnie budujące są wiadomości o kolejnych tive-C. Nie poprzestawaj na niej. Cocos2D to biblioteka,
zbudowanych na bazie Cocosa tytułch, które odniosły która żyje. Nowe samouczki powstają z dnia na dzień;
spektakularny sukces na AppStore); tutaj znajdziesz nowe książki się piszą. Nie bój się zatem korzystać z wy-
również obszerną dokumetację techniczną biblioteki, szukiwarki; jest szansa, że jeśli nie dziś, to jutro znajdziesz
■■ http://www.raywenderlich.com/: blog techniczny, na interesujące Cię informacje. Zachęcam również do ak-
którym publikowane są na bieżąco samouczki adre- tywnego korzystania z forum dyskusyjnego biblioteki do-
sowane do programistów aplikacji na platformę iOS; stępnego pod adresem http://www.cocos2d-iphone.org/
duża część z tych samouczków poświęcona jest wła- forum/. Na pewno też możesz się spodziewać kolejnych
śnie Cocosowi; zarówno ilość, jak i jakość prezentowa- artykułów traktujących o Cocosie na łamach Programisty.
nych tam materiałów robi wrażenie,
■■ http://www.learn-cocos2d.com/: strona prowadzona PODSUMOWANIE
przez Steffena Itterheima, współautora książki Learn
Cocos2D; można tu znaleźć szereg interesujących ar- Czytając niniejszy artykuł, zaznajomiłeś się z podstawo-
tykułów na temat Cocosa; Steffen jest również auto- wymi koncepcjami, na których opiera się Cocos2D, po-
rem bardzo ciekawego silnika Kobold2D, stanowiącego znałeś również zakres jego możliwości, mam nadzieję,
rozszerzenie Cocosa, że co najmniej na tyle, aby odpowiedzieć sobie na py-
■■ Learning Cocos2D, Addison Wesley, 2011: książka au- tanie o przydatność tej biblioteki przy realizacji Twoich
torstwa Roda Strougo oraz Ray'a Wenderlicha; świetne własnych przedsięwzięć. Realizacji tych ostatnich życzę
wprowadzenie do Cocosa, pokazane na przykładzie stwo- Ci z całego serca!
Rafał od dziesięciu lat pracuje w branży związanej z produkcją oprogramowania. Jego za-
wodowe zainteresowania skupiają się przede wszystkim na nowoczesnych technologiach
mobilnych oraz na programowaniu gier. Rafał pracuje aktualnie jako Techniczny Koordy-
nator Projektu w firmie BLStream.
Ł A C A J
TRENERZY SZKOLEŃ
STACJONARNYCH NIE PRZEP N I A
Z K O L E
ZA S Z N E!
A M IS T Y C
PR O G R
OFERTA SZKOLEŃ
w w w. d e v c a s t z o n e . c o m
JĘZYKI PROGRAMOWANIA
Bartosz Szurgot, Mariusz Uchroński, Wojciech Waga
C++11 - część I
C++11 to nowy standard języka C++, zatwierdzony przez komitet ISO 12 sierpnia
2011 roku. Ponieważ zawiera on dużo zmian i nowości, zarówno po stronie samego
języka, jak i biblioteki standardowej, warto się z nimi zapoznać już teraz.
J
ęzyk C++ od lat zajmuje czołowe pozycje na listach Dodatkowo fakt jego dostępności, zarówno dla platformy
popularności języków programowania. Trudno się Linux, jak i Windows oraz możliwość darmowego stoso-
temu dziwić, gdyż łączy on możliwości tworzenia roz- wania, czyni z niego niemal idealnego kandydata do two-
budowanych abstrakcji, charakterystycznych dla języków rzenia przykładów.
wysokiego poziomu, z wydajnością języków niskopozio- Po ostatniej konferencji C++ and Beyond, podczas wy-
mowych. Ponad pół roku temu w końcu miało miejsce wiadu Herb Sutter został zapytany, kiedy, jego zdaniem,
wydarzenie, na które programiści C++ czekali od wielu większość kompilatorów będzie wspierać nowy standard.
lat - został ogłoszony nowy standard języka, nazywany Odpowiedział, że prawdopodobnie za około rok. Dla mniej
potocznie C++11. Nowy standard niesie ze sobą dużo popularnych platform - około dwóch lat. By nie czekać tak
zmian. Wprowadzono wiele nowych mechanizmów, takich długo, zapraszamy do poznawania elementów C++11,
jak na przykład wątki czy wyrażenia lambda. Poszerza oferowanych przez GCC, już dziś. Zatem do dzieła...
także zakres stosowalności i ogólności znanych już me-
chanizmów, takich jak na przykład szablony czy tworze- GENERACJA LICZB
nie skróconych nazw dla typów. PSEUDOLOSOWYCH
Duża część mechanizmów C++11 została przedstawio-
na w cyklu artykułów C++0x (w chwili pisania części z nich Na początku należy uświadomić czytelnikowi, że progra-
standard nie był jeszcze zatwierdzony), opublikowanych mowe generatory liczb losowych tak naprawdę nie gene-
na łamach czasopisma Software Developer`s Journal, w rują prawdziwych liczb losowych, dlatego nazywane są
drugiej połowie 2011 r. oraz na początku 2012 r. Niniejszy generatorami liczb pseudolosowych (ang. PRNG - Pseu-
cykl jest kontynuacją prezentacji Nowej Twarzy C++. Au- do Random Number Generator). Generatory programo-
torzy zalecają zapoznanie się z materiałami dotyczącymi we oparte są na formułach matematycznych, które dają
C++11, w szczególności referencjami do r-wartości (ang. wrażenie losowości. Oprócz generatorów programowych
rvalue reference), odwołania do których pojawiają się istnieją także sprzętowe generatory liczb losowych (ang.
w niniejszym tekście. Ułatwi to zrozumienie tekstu i od- TRNG - True Random Number Generator), których dzia-
krycie nowych możliwości zastosowań prezentowanych łanie jest oparte na zasadzie obrazowania właściwości
mechanizmów. i parametrów fizycznego procesu stochastycznego, naj-
Z założenia cykl ten ma prezentować praktyczne zasto- częściej szumu elektrycznego. Programowe generowanie
sowania, nie zaś suchą teorię. Dlatego, spośród licznych liczb pseudolosowych, o wysokich parametrach, jest więc
rozszerzeń, zarówno samego języka, jak i jego biblioteki dość skomplikowanym problemem.
standardowej, wybór autorów pada na elementy dostęp- Przed pojawieniem się standardu C++11 istniał tylko
ne i wspierane przez istniejące kompilatory. W chwili pi- jeden sposób na generację liczb pseudolosowych w C++.
sania tych słów zdecydowanie najlepsze wsparcie, wśród Mowa tutaj o użyciu funkcji srand() oraz rand(), z bi-
kompilatorów, z jakimi autorzy mieli styczność, ma GCC. blioteki C. W celu inicjalizacji generatora liczb pseudolo-
sowych należało jednarazowo wywołać funkcję srand().
Jako parametr do tej funkcji należy przekazać liczbę typu
Kompilowanie przykładów unsigned int tzw. seed. Należy zwrócić szczególną uwa-
gę na wartość seeda, ponieważ w przypadku, gdy wartość
Do kompilowania przykładów należy użyć kompilato ta będzie stałą, to przy każdym uruchomieniu programu
ra GCC w wersji 4.6, ustawiając standard języka na C++0x: ciąg wygenerowanych liczb pseudolosowych będzie taki
g++-4.6 -Wall -std=c++0x src.cpp sam. Z tego powodu jako wartość seed podawany jest
Większość prezentowanych fragmentów kodu NIE będzie bieżący czas systemowy. Po zainicjalizowaniu generatora
działać na starszych wersjach, które miały minimalne lub liczby pseudolosowe można generować przy użyciu funk-
żadne wsparcie dla C++0x. W przypadku nowszych wersji cji rand(). Na Listingu 1a zostało przedstawione użycie
GCC niektóre przykłady mogą wymagać drobnych modyfi funkcji srand() do inicjalizacji generatora liczb pseudo-
kacji. Pamiętajmy, że w chwili wydania GCC 4.6 C++0x nie losowych wartością bieżącego czasu systemowego oraz
był zatwierdzonym standardem. Oprócz tego GCC nie jest użycie funkcji rand() do generacji liczb pseudolosowych.
jeszcze w pełni zgodny z propozycją standardu języka. Wywołanie funkcji time(nullptr) pozwala na uzyskanie
bieżącego czasu systemowego.
Listing 1a. Przykład inicjalizacji i użycia generatora liczb Szablon linear_congruential_engine wymaga mini-
losowych w starym stylu malnej ilości pamięci do przechowywania swojego stanu.
Stan generatora opartego o ten szablon przechowywany
int randBetween(const int &min, const int &max)
{ jest w postaci pojedynczej liczby całkowitej, zawierają-
return (rand() % (static_cast<int>(max - min + cej poprzednio wygenerowaną liczbę pseudolosową, lub
1)) + min); początkowy seed, jeżeli żadna liczba pseudolosowa nie
}
została jeszcze wygenerowana. Okres takiego generatora
const int min = 1;
const int max = 10; zależy od użytych parametrów algorytmu i może wynosić
srand(static_cast<unsigned int>(time(nullptr))); do 264, ale najczęściej jest mniejszy. Z tego powodu ge-
cout << rand() << endl; neratory oparte o szablon linear_congruential_engine
cout << randBetween(min, max) << end
nie generują liczb pseudolosowych o dużej losowości.
Funkcja rand() pozwala na generację liczb pseudoloso- Szablon mersenne_twister_engine pozwala na ge-
wych z zakresu od 0 do RAND_MAX. Standard określa war- nerację liczb pseudolosowych o największej losowości,
tość RAND_MAX na wartość co najmniej 32767. Niestety a okres generowanych liczb jest dużo większy niż dla sza-
często wartości najmłodszych bitów dla liczb generowa- blonu linear_congruential_engine. Rozmiar pamięci
nych przez funkcję rand() są mało losowe. Oznacza to, potrzebny do przechowywania stanu generatora opartego
że użycie funkcji randBetween(...) do generacji liczb o szablon mersenne_twister_engine zależy od wartości
z małego zakresu (np. [1,5]) skutkuje niezbyt wysoką użytych parametrów i jest dużo większy niż w przypad-
losowością wygenerowanych liczb. Warto zwrócić uwagę ku szablonu linear_congruential_engine. Na przykład
na fakt, że funkcje srand() oraz rand() nie pozwalają predefiniowany generator mt19937, oparty o szablon
na zmianę rozkładu prawdopodobieństwa generowanych mersenne_twister_engine, ma okres generowanych
liczb pseudolosowych. liczb pseudolosowych równy 219937-1, podczas gdy jego
Dlatego standard C++11 został wzbogacony o rozbu- stan przechowywany jest na 624 liczbach całkowitych,
dowaną bibliotekę do generacji liczb pseudolosowych, o czyli w około 2.5kB pamięci.
różnych rozkładach prawdopodobieństwa, z wykorzysta- Szablon subtract_with_carry_engine przechowu-
niem różnych algorytmów. Biblioteka ta została zdefinio- je stan generatora na 25 liczbach całkowitych (na oko-
wana w pliku nagłówkowym random. ło 100B pamięci). Jakość liczb pseudolosowych genero-
Standard C++11 definiuje następujące klasy i szablo- wanych przez generator, oparty na tym szablonie, jest
ny generatorów liczb pseudolosowych: jednak gorsza, niż dla generatora opartego o szablon
mersenne_twister_engine.
■■ random_device, Klasa random_device jest prosta w użyciu i nie wy-
■■ linear_congruential_engine, maga żadnych parametrów. Natomiast utworzenie ge-
■■ mersenne_twister_engine, neratora liczb pseudolosowych w oparciu o pozosta-
■■ subtract_with_carry_engine. łe szablony wymaga podania wielu parametrów, które
mogą być skomplikowane. Dobór parametrów ma
Klasa random_device nie jest programowym generato- wpływ na jakość generowanych liczb pseudolosowych.
rem liczb pseudolosowych, wymaga on specjalnego sprzę- Na przykład szablon mersenne_twister_engine wy-
tu pozwalającego wygenerować niedeterministyczne licz- maga zdefiniowania 14 parametrów. Podobnie szablony
by losowe. Zgodnie z definicją szablonu random_device, linear_congruential_engine oraz subtract_with_
jeżeli komputer nie jest wyposażony w sprzętowy ge- carry_engine wymagają podania dużej liczby parame-
nerator liczb losowych, to biblioteka wykorzysta jeden trów w celu zdefiniowania konkretnego generatora. Dla-
z generatorów programowych (wykorzystanie konkret- tego standard zawiera predefiniowane generatory liczb
nego generatora zależy od implementacji biblioteki). Ja- pseudolosowych. Są to:
kość liczb pseudolosowych wygenerowanych z użyciem
szablonu random_device związana jest z jego entropią, ■■ minstd_rand0,
której wartość można uzyskać poprzez wywołanie funkcji ■■ minstd_rand,
entropy(). Jeśli funkcja entropy() zwróci wartość 0, to ■■ mt19937,
oznacza to, że do generacji liczb pseudolosowych został ■■ mt19937_64,
wygenerowany generator programowy. Użycie szablonu ■■ ranlux24_base,
random_device został przedstawiony na Listingu 1b. ■■ ranlux48_base.
Listing 1b. Genaracja liczb pseudolosowych przy użyciu Aby wygenerować liczbę pseudolosową w C++11, należy
srand( )/rand( ) utworzyć instancję generatora liczb pseudolosowych oraz
dla generatorów programowych określić rozkład prawdo-
random_device rnd;
cout << "Entropia: " << rnd.entropy() << endl; podobieństwa. Najprostszą metodą utworzenia generatora
cout << "Wartość minimalna: " << rnd.min() liczb pseudolosowych jest wykorzystanie jednego z pre-
<< " wartość maksymalna: " << rnd.max() << endl; definiowanych generatorów. W Listingu 1c został wyko-
cout << "Liczba pseudolosowa: " << rnd() << endl;
rzystany predefiniowany generator liczb pseudolosowych
/ www.programistamag.pl / 23
JĘZYKI PROGRAMOWANIA
mt19937, oparty o szablon mersenne_twister_engine. Lista zawiera najczęściej spotykane w statystyce rozkła-
Podobnie, jak w przypadku generacji liczb pseudolosowych dy. Programista otrzymuje więc zestaw gotowych narzę-
z wykorzystaniem funkcji srand() i rand(), należy za- dzi do pracy z liczbami losowymi!
inicjalizować generator. W przykładzie generator mt19937 Jako przykład niech posłuży kod z Listingu 1e, służący
został zainicjalizowany wartością bieżącego czasu syste- do generacji miliona liczb losowych o rozkładzie normal-
mowego. Następnie został zdefiniowany rozkład prawdopo- nym, wartości oczekiwanej równej 50 oraz wariancji rów-
dobieństwa, w tym przypadku jest to rozkład jednostajny o nej 6. Jako generator liczb pseudolosowych użyty został
zakresie od 1 do 99. W celu wygenerowania liczby pseudo- typ mt19937. Wektor mn przechowuje liczbę wystąpień
losowej należy wywołać obiekt funkcyjny, zdefiniowanego liczb z zakresu od 1 do 100. Następnie na ekran wypisy-
wcześniej rozkładu prawdopodobieństwa, jako argument wana jest zawartość owego wektora.
podając instancję generatora liczb pseudolosowych.
Listing 1e. Przykład generowania liczb pseudolosowych o
Listing 1c. Przykład generowania liczby pseudolosowej, o rozkładzie normalnym
rozkładzie jednostajnym
#include <random>
mt19937 eng(static_cast<unsigned #include <functional>
long>(time(nullptr))); #include <iostream>
uniform_int_distribution<int> dist(1, 99); using namespace std;
cout << dist(eng) << endl; int main(void)
{
const int max = 100;
Istnieje możliwość wyeliminowania konieczności określa- // liczba losowań
const int it = 1000000;
nia generatora liczb pseudolosowych i rozkładu prawdopo-
// określenie rozkładu prawdopodobieństwa
dobieństwa w momencie generacji liczby pseudolosowej. normal_distribution<double> normal(50.0, 6.0);
Efekt taki można uzyskać poprzez użycie szablonu bind(), // utworzenie instancji generatora liczb
zdefiniowanego w pliku nagłówkowym functional. Przy- // pseudolosowych
mt19937 enginei(static_cast<unsigned
kład użycia funkcji bind(), w kontekście generacji liczb long>(time(nullptr)));
pseudolosowych, został przedstawiony na Listingu 1d. // 'zbindowanie' generatora z rozkładem
// prawdopodobieństwa
auto rndi = bind(normal, enginei);
Listing 1d. Użycie funkcji bind(...) w kontekście generowania
// wektor przechowujący informację o częstości
liczb pseudolosowych
// występowania losowanych liczb
vector<int> mn(max+1);
mt19937 eng(static_cast<unsigned // generacja liczb pseudolosowych
long>(time(nullptr))); for (unsigned int i = 0; i<it; ++i)
uniform_int_distribution<int> dist(1, 99); ++mn.at(rndi());
auto gen = bind(dist, eng); //
cout << gen << endl; for (int i = 1; i<mn.size(); ++i)
cout << i << " " << mn[i] << endl;
return 0;
W standarcie C++11 zostały zdefiniowane różne rozkłady }
prawdopodobieństwa:
Aby lepiej unaocznić rozkłady prawdopodobieństwa, war-
■■ uniform_int_distribution, to się przyjrzeć jego graficznej reprezentacji, dla różnych
■■ uniform_real_distribution, zestawów parametrów. Na Rysunku 1a przedstawiony zo-
■■ bernoulli_distribution, stał przykład rozkładu jednostajnego. Rysunek 1b przed-
■■ binomial_distribution, stawia rozkład normalny, znany także pod nazwą rozkła-
■■ geometric_distribution, du Gaussa. Wykładniczy rozkład prawdopodobieństwa
■■ negative_binomial_distribution, ilustruje Rysunek 1c.
■■ poisson_distribution, Wartym uwagi jest fakt, iż C++11, wymuszając jawne
■■ exponential_distribution, tworzenie generatorów liczb pseudolosowych, rozwiązu-
■■ gamma_distribution, je jednocześnie dwa problemy, spotykane w wielu ge-
■■ weibull_distribution, neratorach (także niektórych z biblioteki C). Pierwszym,
■■ extreme_value_distribution, dość oczywistym, jest rozdzielenie samego generatora
■■ normal_distribution, (rozumianego jako ,,źródło pseudolosowości'') od kodu
■■ lognormal_distribution, generującego pożądany rozkład generowanych liczb. Ta-
■■ chi_squared_distribution, kie podejście jest znaczącą poprawą architektury, zwięk-
■■ cauchy_distribution, szającym elastyczność i umożliwiającym proste tworze-
■■ fisher_f_distribution, nie własnych rozkładów, w miarę potrzeb. Drugą istotną
■■ student_t_distribution, korzyścią jest możliwość tworzenia osobnych (tj. nieza-
■■ discrete_distributio, leżnych) generatorów w różnych wątkach. Programiści
■■ piecewise_constant_distribution, tworzący aplikacje obliczeniowe, korzystające z PRNG
■■ piecewise_linear_distribution. i wątkowania, często spotykali się z problemem niejawnej
Rysunek 1a. Jednostajny rozkład prawdopodobieństwa Rysunek 1b. Normalny rozkład prawdopodobieństwa
/ www.programistamag.pl / 25
JĘZYKI PROGRAMOWANIA
zwracanego. Typ ten jest jawnie podany jako tak zwa- kretny już typ argumentów ustalony w czasie kompilacji.
ny trailing-return-type. Listing 2b pokazuje bardziej Rozwiązanie to jest bardzo elastyczne, gdyż pozwala na
użyteczne zastosowanie nowej składni, którego nie dało- stworzenie pomocniczego szablonu, który „wyekstrahuje“
by się zapisać inaczej. np. typ argumentu podanej funkcji. Wadą tego rozwią-
zania jest to, że jest mało czytelne, nie ma możliwości
Listing 2b. Alternatywna składnia funkcji przekazania ustalonego typu poza pomocniczą funkcję
szablonową, a do tego wywołanie wymusza ewaluację
template<class A, class B> wyrażenia, co nie zawsze jest pożądane. Rozwiązanie nr
auto sum(const A &a, const B &b) -> decltype(a+b)
{ 2 wydaje się być pozbawione tych problemów, jest jednak
return x+y; uzależnione od konwencji kodowania. I jeżeli autor kolek-
} cji nie zadeklaruje value_type jako typu elementu swo-
jej kolekcji, metoda ta nie zadziała. Rozwiązanie nr 3 jest
Tutaj wartość zwracana ustalana jest za pomocą opera- mniej elastyczne niż pierwsze, bo nie pozwoli już określić
tora decltype opisanego w kolejnym punkcie. Operator typu argumentu funkcji. Jest ono również nieprzenośne,
ten nie może stać przed nazwą funkcji, gdzie znajduje się ponieważ korzysta z rozszerzenia kompilatora. Wymaga
typ w klasycznej deklaracji, gdyż tam zmienne a i b nie także napisania wyrażenia dwa razy, co z kolei utrudnia
są jeszcze znane. utrzymywanie kodu.
Mimo swojej zwięzłości, przykład z Listingu 2b nie ob-
razuje wcale trywialnej sytuacji. Typy szablonowe A oraz Listing 3b. Operacje na kolekcji elementów dowolnego typu
B mogą być różne, co wyklucza podanie jednego z nich
jako wartości zwracanej. Dodatkowo może być zdefinio- template<class T>
void real_swap(T &x, T &y)
wany operator dodawania dla tychże dwóch typów, zwra-
{
cający wartość zupełnie innego typu niż A lub B. T temp=x;
x=y;
DECLTYPE y=temp;
}
/ www.programistamag.pl / 27
JĘZYKI PROGRAMOWANIA
sposoby: stosując jawne podanie typu (Listing 4b) lub Ciekawym rozszerzeniem składni C++11 jest też do-
używając nowych metod kontenerów standardowych: danie pętli for, automatycznie iterującej po elementach
cbegin() oraz cend(). tablic i kontenerów. Taki zwięzły zapis nie tylko pozwoli
na skrócenie zapisu standardowych konstrukcji języka,
PODSUMOWANIE ale także fenomenalnie uprości tworzenie uogólnionego
kodu. Od tej pory szablony nie będą musiały już spraw-
W niniejszym artykule przedstawione zostały nowe elemen- dzać, czy dany element jest tablicą czy też kontenerem.
ty języka C++11. Bardzo dużą i ważną zmianą jest doda- Do iterowania wystarczy nazwa samego obiektu!
nie generatorów liczb pseudolosowych do biblioteki stan-
dardowej. Uczynienie tak ważnego elementu elastycznym CIĄG DALSZY
i standardowym z pewnością przyczyni się do powstawania
wyższej jakości kodu oraz, na co autorzy mają nadzieję, W następnym odcinku niniejszego cyklu opisany zosta-
mniejszej liczby luk bezpieczeństwa z nimi związanych. nie mechanizm cech typów. Jest to kolejny ukłon w stro-
Niemałym plusem, dla osób tworzących metaprogramy, nę tworzących metaprogramy, mający ułatwić spraw-
jest pojawienie się konstrukcji decltype, pozwalającej dzanie własności parametrów. Omówione zostaną także
na ,,sprawdzanie'' typu wyrażenia w czasie kompilacji. W rozszerzenia składni dotyczące typów wyliczeniowych
połączeniu z alternatywną składnią funkcji, programista oraz reprezentacji i mierzenia czasu. Na koniec pojawi
otrzymuje potężne narzędzie, umożliwiające pisanie bar- się także kilka drobnych, lecz bardzo przydatnych roz-
dziej generycznego kodu, który stanie się jeszcze prost- szerzeń: jawne operatory konwersji oraz nowe modyfi-
szy w utrzymaniu. katory metod.
bartek.szurgot@baszerr.eu
Bartosz Szurgot http://www.baszerr.eu
W
obecnym okresie język Objective-C bardzo szyb- różnic i niuansów w porównaniu do Javy czy C (więcej
ko zyskuje na popularności. Spowodowane jest szczegółowych informacji można znaleźć w serwisie:
to głównie ekspansją na rynku produktów firmy https://developer.apple.com).
Apple, jak iPhone, iPad czy iTouch. Po wykupieniu progra-
mu developerskiego Apple'a, możemy dystrybuować wła- BLOKI
sną aplikację i zarabiać na tym niemałe pieniądze. Jako
programista natywnych aplikacji warto znać kilka waż- Artykuł rozpoczniemy od bloków. Blok jest obiektem, któ-
nych i przydatnych elementów języka, które mogą bardzo ry zamyka w sobie jakąś jednostkę (część) kodu. Jest
ułatwić nam przyszłą pracę przy rozwijaniu naszych sys- tzw. segmentem kodu, który może być w dowolnym mo-
temów. Elementy te mogą także zaciekawić osoby dopie- mencie uruchomiony ( i wykonany w kontekście imple-
ro zaczynające przygodę z platformą iOS. W niniejszym mentowanej metody) lub przekazany jako argument do
artykule postaram się zaprezentować elementy języka, funkcji. Tak przekazywany argument można dalej trakto-
które są bardzo istotne podczas rozwijania oraz utrzymy- wać jak każdy inny obiekt. Jest to wygodne i przydatne
wania projektów na mobilne platformy. W szczególności rozwiązanie.
będą to: Bloki są cechą języka C, które zostały dodane do
iPhone i iPad SDK w wersji iOS 4. W języku Objective-C
■■ programowanie z użyciem bloków, „bloki” to inaczej domknięcia leksykalne (ang. closures)
■■ kategorie i ich wykorzystanie, lub funkcjonujące w innych językach wyrażenia lambda.
■■ protokoły oraz selektory w Objective-C, Każde takie wyrażenie posiada zasięg leksykalny zakre-
■■ zarządzanie pamięcią w Objective-C, su, w którym zostało stworzone (tak więc jeśli w bloku
■■ użyteczne makra w Objective-C. domknięcia użyjemy zmiennej lokalnej, będzie ona ist-
nieć do chwili destrukcji samego wyrażenia lambda).
Na początku warto krótko wspomnieć, czym jest język Zamiast używać funkcji zwrotnych wymagających odpo-
Objective-C. Jest to rozszerzenie języka C o możliwo- wiedniej struktury danych (reprezentujących wszystkie
ści obiektowe, wzorowane na Smalltalku. Język obrał informacje kontekstowe potrzebne do wykonania opera-
odmienną drogę od C++. Jest używany głównie w fra- cji), wystarczy mieć możliwość bezpośredniego dostępu
mework'u Cocoa w systemie Mac OS X oraz w iOS. Ob- do zmiennych lokalnych. Bloki umożliwiają taki dostęp,
jective-C jest głównym językiem programowania na więc stwarza nam to wielkie możliwości na wykorzystanie
iPhone'a. Jest on rozszerzeniem języka ANSI C o moż- bloków.
liwości programowania obiektowego. Zatem dostajemy Na Listingu 1 został przedstawiony przykład deklaracji
dziedziczenie, enkapsulacje czy polimorfizm. Elemen- i użycia bloku.
ty składniowe, o jakie rozszerzono w tym celu język C,
używają dwóch symboli: [] oraz @ (rozszerzeń składni Listing 1. Przykład deklaracji oraz użycia bloku kodu
jest dużo więcej, ale tylko te wchodzą w jakiekolwiek
interakcje ze składnią języka C). Nawiasy kwadratowe // plik implementacji (*.m)
int (^sum) (int, int) = ^(int arg1, int arg2){
są używane do wywoływania metod, natomiast @ do de- return arg1 + arg2;
finicji specyficznych dla języka Objective-C. Istnieją też };
specjalnie dla Objective-C wprowadzone typy, istnieją- int result = sum( 1, 2);
NSLog(@"suma wynosi: %d", result);
ce już według reguł języka C, z których najważniejszym
jest id. Typ ten jest uniwersalną "referencją do obiektu"
(dokładnie to wskaźnikiem, z punktu widzenia języka W przykładzie z Listingu 1 deklarujemy (i wykorzystuje-
C). Ponieważ nie jest to artykuł traktujący o zmianach my) blok przyjmujący dwa argumenty typu int i zwraca-
(i o samym języku), które wprowadza język, lecz o przy- jący wartość typu int. Znak ^ jest wymagany do zdefi-
datnych elementach, nie będę więc zgłębiał wszystkich niowania bloku. Przykład jest generalnie poglądowy i nie
ma większego sensu czy znaczenia z punktu widzenia in- Listing 4. Pobieranie bloków
żynierii programowania.
Prawdziwą siłę bloków można doświadczyć, używając // pobranie bloku bez argumentów
- (void)someMethod:(void(^)())block;
ich jako zwrotnych (callback'owych), kiedy inna operacja
została zakończona, przekazywanych do funkcji jako ar- // pobranie bloku z argumentami
gument. Mogą wiec być alternatywą dla delegacji w przy- - (void)someMethod:(void(^)(NSString *test, id
anyObject))block;
padku, gdy zaistnieje potrzeba, kiedy obiekt delegata ma
implementować tylko jedną metodę (Listing 2). // przypadki typu zwracanego
- (void)someMethod:(BOOL(^)())block;
- (void)someMethod:(NSRange *(^)())block;
Listing 2. Przykład wykorzystania bloku
// wywołanie bloku
// istnieje możliwość zdefiniowania typu bloku za - (void)someMethod:(void(^)(NSString *test))block {
pomocą słówka typedef block(@"Przykładowy tekst w bloku");
typedef int (^Multiply)(int, int); }
// w takiej sytuacji możemy zdefiniować metodę Listing 5. Używanie bloków zwróconych z metod
// przyjmującą blok jako argument, w ten sposób
- (void) multiplyMethod: (Multiply) mult;
// przypisanie do tablicy - bloku
- (void)registerCallback:(BOOL(^)())callback {
// implementacja mogłaby wyglądać następująco
[_listeners addObject:[[callback copy]
- (void) multiplyMethod: (Multiply) mult{
autorelease]];
int a, b;
}
a = 4;
b = 6;
- (void)runCallbacks {
NSLog(@"Wynik operacji %d * %d wynosi: %d", a,
int i = [_listeners count];
b, mult(a,b));
while (i--) {
}
BOOL (^block)() = [_listeners
objectAtIndex:i];
// następnie w kodzie możemy opisać blok i użyć
BOOL state = block();
naszej metody
// tutaj dalsza część kodu ...
Multiply myMult = ^(int arg1, int arg2){
}
return arg1*arg2;
}
};
[self multiplyMethod:myMult];
Wszystko, co możemy zrobić, wykorzystując bloki, moż-
na również zrobić w inny sposób. Stwierdzenie to jest
Definicja bloku następuje po słowie kluczowym typedef, prawdą, bloki jednak dostarczają bardzo użytecznej
po czym specyfikujemy strukturę bloku: możliwości na uproszczenie kodu i uczynienia rzeczy
bardziej przejrzystymi. Warto więc poeksperymentować
typedef int (^Multiply)(int, int); na kilku przykładach, a po pewnym czasie ciekawe roz-
wiązania z pewnością się pojawią. Na przykład, załóżmy,
Gdybyśmy nie zdefiniowali typu bloku za pomocą typedef, że mamy połączenie URL i chcemy poczekać na wynik.
deklaracja funkcji musiałaby wyglądać następująco (czyli Dwa popularne podejścia to: zapewnienie callback'u de-
zawierać całą specyfikację dla bloku): legata lub użyć bloku. Wykorzystamy do zaprezentowa-
nia tego fikcyjną klasę URLConnection jako przykład
- (void) multiplyMethod:(int (^)( int a, int b)) mult; (Listing 6).
Bloki są cechą języka C, które zostały dodane do iPhone Listing 6. Przykład wykorzystania delegata
i iPad SDK w wersji iOS 4. Pozwalają na stworzenie „blo-
ku” kodu, który można dalej przekazać, jak każdy obiekt URLConnection* someConnection=[[[URLConnection
alloc] initWithURL:someURL] autorelease];
(jest to wygodne i przydatne rozwiązanie).
someConnection.delegate=self;
[someConnection start];
Listing 3. Przekazywanie bloku // następnie w danej klasie
// obsługujemy metodę delegata
// bardzo prosty przypadek - (void)connection:(URLConnection)connection
[foo someMethod:^{ didFinishWithData:(NSData*)
// pożądany kod w tym miejscu {
}]; // wykonaj działania na danych
}
// blok pobierający argumenty
[foo someMethod:^(NSString *var1, BOOL var2) {
// var1 oraz var2 mogą być użyte Natomiast w przypadku korzystania z bloku, można osa-
// jak argumenty w funkcji
dzić kod, który jest wywoływany dokładnie tam, gdzie
}];
tworzymy połączenie (Listing 7).
/ www.programistamag.pl / 31
JĘZYKI PROGRAMOWANIA
Listing 7. Przykład wykorzystania bloku Listing 9. Przykład kategorii MathUtilities na klasie NSNum-
ber
URLConnection* someConnection = [[[URLConnection
alloc] initWithURL:someURL] autorelease]; // plik nagłówkowy (*.h)
someConnection.successBlock = ^(NSData*)data {
// wykonaj działania na danych #import <Foundation/Foundation.h>
}; @interface NSNumber (MathUtilities)
[someConnection start]; - (float) toRadians;
- (float) toDegrees;
@end
Dodatkowo załóżmy, że mamy wiele połączeń w swojej
// NSNumber+MathUtils.m
klasie i wszystkie potrzebujemy obsłużyć przy użyciu tego
#import "NSNumber+MathUtils.h"
samego delegata. Teraz więc musimy rozróżnić poszcze- @implementation NSNumber (MathUtilities)
gólne połączenia w naszej metodzie delegata. To może - (float) toRadians
się komplikować, im więcej połączeń jest dostępnych (Li- {
return [self floatValue] * (M_PI/180);
sting 8). }
- (float) toDegrees
Listing 8. Obsługa metody w podejściu z delegatem {
return [self floatValue] * (180/M_PI);
}
- (void)connection:(URLConnection)connection
@end
didFinishWithData:(NSData*)
{
// przykład użycia nowej metody w kodzie
if(connection == self.connection1)
...
{
NSNumber *myDegrees = [NSNumber
// wykonaj działania na danych z connection1
numberWithFloat:180];
}
NSLog(@"Radiany: %.2f", [myDegrees toRadians]);
if(connection == self.connection2)
...
{
// wykonaj działania na danych z connection2
}
if(connection == self.connection3) Kategorie mogą być również wykorzystywane do za-
{ stąpienia / nadpisania metod, które dziedziczą z klasy
// wykonaj działania na danych z connection3 bazowej (co stanowi alternatywę do tworzenia podkla-
}
...
sy). Zawsze można uzyskać dostęp do nadpisanej me-
} tody przy użyciu specyfikatora super. Przypuszczalnie
z perspektywy kompilatora (code generation) może
Wykorzystując podejście korzystające z bloku, można użyć prowadzić do mniejszego narzutu kodu w stosunku do
unikalnego bloku na dane połączenie URL, co jest bardziej opcji tworzenia podklasy (wyłącznie po to, aby zastąpić
przejrzyste w trakcie późniejszego utrzymania kodu. metodę).
Powyżej przedstawiłem krótki opis oraz przykładowe Metody w kategoriach są nierozróżnialne z metodami
wykorzystanie bloków, o których oczywiście można pisać klasy - w szczególności mają dostęp do zmiennych pry-
o wiele więcej, myślę jednak, że taka dawka informacji watnych. Kategorie mogą również przedefiniować istnie-
powinna zaciekawić Czytelnika do dalszego zgłębienia jące już metody (np. mogą poprawić błędną implementa-
tematu. Szczegółowych informacji można wyszukać na: cję lub rozszerzyć funkcjonalność). Należy zaznaczyć, iż
https://developer.apple.com/resources/ w dziale: Blocks w sytuacji, gdy mamy dwie kategorie, które przedefinio-
Programming Topics. wują tę samą metodę - specyfikacja języka nie określa
dokładnie, która z metod będzie użyta.
KATEGORIE Kategorie stanowią ciekawą możliwość rozproszenia
realizacji klasy na jednym lub kilka plików. Objective-C
Jednym z problemów, przed którym stanęli projektanci Programming Guide wymienia kilka zalet tego podejścia,
języka Objective-C, było zadbanie o ułatwienie kontroli w tym:
nad dużymi fragmentami kodu, gdyż był to powszechny
problem w przypadku programowania strukturalnego. ■■ możliwość grupowania metod, które wykonują podob-
Kategorie to fragmenty klasy odpowiedzialne za okre- ne zadania,
śloną funkcjonalność, którą realizuje klasa (przykładowo ■■ konfiguracji klasy dla różnych zastosowań, zachowując
klasa NSString może mieć część odpowiedzialną za tłu- jednak jeden zestaw kodu.
maczenie tekstu). Co więcej metody kategorii dodawane
są w czasie wykonania. W związku z tym metody mogą Tworzenie kategorii jest sposobem na rozdzielenie imple-
być dodane do istniejących klas bez rekompilacji i bez mentacji metod na kilka części. Pozwala to na:
dostępu do źródła.
Przykładem jest kategoria MathUtilities, która dodaje do ■■ oddzielną kompilację,
klasy NSNumber metody (toRadians, toDegrees) przydatne ■■ grupowanie metod, które dzięki temu są jasno
podczas zamiany stopni na radiany i odwrotnie (Listing 9). rozdzielone,
■■ klasy mogą być rozszerzane odmiennie w różnych apli- Listing 11. Przykład implementacji kategorii
kacjach bez konieczności duplikowania kodu,
■■ każda klasa może zostać rozszerzona - nawet klasy // plik implementacji (*.m)
@implementation NSString (reverse)
z framework'u Cocoa. -(NSString *) reverseString
{
Jako alternatywę dla tworzenia podklas, kategorie w Ob- NSMutableString *reversedStr;
int len = [self length];
jective-C zapewniają możliwość dodawania nowych me-
// automatycznie zwalniany obiekt NSString
tod w danej klasie. Interesujące jest to, iż wszelkie me- reversedStr = [NSMutableString
tody, które są dodawane w danej kategorii, stają się stringWithCapacity:len];
// mało efektywne
częścią bazowej klasy (którą kategoria rozszerza). Inny-
while (len > 0)
mi słowy, jeśli dodać metodę do klasy NSString, to każ- [reversedStr appendString:
da instancja lub podklasa utworzona z NSString będzie [NSString stringWithFormat:@"%C", [self
mieć dostęp do tej metody. Definiowanie kategorii jest characterAtIndex:--len]]];
return reversedStr;
identyczne do zdefiniowania interfejsu dla klasy. Niewiel- }
kim wyjątkiem jest tutaj to, iż nazwę kategorii potrzeba @end
wyspecyfikować wewnątrz nawiasów: (nazwa) tuż po
deklaracji interfejsu. Przykładowyf format został przed- Listing 12. Przykład użycia zaimplementowanej kategorii
stawiony na Listingu 10. (reverse)
#import <Foundation/Foundation.h>
Listing 10. Przykład definiowania kategorii #import "NSString+Reverse.h"
int main (int argc, const char * argv[])
// plik nagłówkowy (*.h) {
// ogólny schemat definiowania kategorii NSAutoreleasePool *pool = [[NSAutoreleasePool
@interface ClassToAddMethodsTo (category) alloc] init];
// ... lista metod NSString *str = [NSString
@end stringWithString:@"Tekst demonstracyjny."];
NSString *rev;
// definicja kategorii reverse dla klasy NSString NSLog(@"String: %@", str);
@interface NSString (reverse) rev = [str reverseString];
-(NSString*) reverseString; NSLog(@"Po odwróceniu mamy: %@",rev);
@end [pool drain];
return 0;
}
Dla przykładu, na Listingu 10 mamy zdefiniowaną kate-
gorię, która dodaje metodę do klasy NSString. Metoda
reverseString dodaje możliwość odwrócenia znaków PROTOKOŁY
w łańcuchu tekstowym dla wszystkich obiektów utworzo-
nych z klasy NSString. Protokół to lista metod, które klasa musi zadeklarować
Podobnie jak w przypadku zmiany w deklaracji kate- i zaimplementować. Od wersji Objective-C 2.0 mogą one
gorii (w sekcji @interface), sekcja @implementation posiadać metody opcjonalne. Protokoły realizują idee
jest w podobny sposób zmodyfikowana. Tuż po nazwie wielokrotnego dziedziczenia specyfikacji, ale nie imple-
klasy dodajemy nazwę kategorii zawartą w nawiasach. mentacji. W C++ ten wzorzec osiąga się poprzez wie-
Na Listingu 11 znajduje się implementacja interfejsu zde- lokrotne dziedziczenie klas bazowych (abstrakcyjnych),
finiowanego wcześniej na Listingu 10. Należy zauważyć, a w C# i w Javie poprzez interfejsy. Przykł. pewna klasa
iż w obu przypadkach została dodana nazwa kategorii GDButton może mieć delegata implementującego proto-
(reverse). kół z opcjonalną metodą showShining. Obiekt GDButton
Zalecana konwencja nazewnicza dla plików kategorii jest może sprawdzić, czy delegat implementuje showShining
następująca: poprzez refleksje, i jeśli tak, to wysyłać odpowiedni ko-
munikat do delegata, by skorzystać z tej funkcjonalności.
"ClassToAddMethodsTo+CatgoryName" Warto zwrócić uwagę na różnice pomiędzy interfejsami
z Javy a protokołami z Objective-C, polegające na tym,
Przykładowo, dla pliku interfejsu i pliku implementacji że w Objective-C klasa może implementować protokół,
kodu kategorii przedstawianego na listingach, zostały wy- nawet jeśli nie zostało to zadeklarowane (w szczególno-
korzystane następujące nazwy: ści, wykorzystując kategorie, możemy nawet poprawić
istniejącą już klasę tak, by zaczęła implementować ja-
NSString+Reverse.h kiś protokół). Można sprawdzić, czy obiekt jest zgodny
NSString+Reverse.m
z określonym protokołem, jak pokazano poniżej:
/ www.programistamag.pl / 33
JĘZYKI PROGRAMOWANIA
Jak można zauważyć w kodzie, specyfikator SEL jest Listing 17a. Przykład alokacji i zwolnienia obiektu
krótszą formą instrukcji @selector(). Selektor można
zapisać jako: NSString *text = [[NSString alloc] init];
// wykonaj działania na zmiennej text
[text release];
SEL sel = @selector(lowercaseString);
/ www.programistamag.pl / 35
JĘZYKI PROGRAMOWANIA
@property (retain) NSString *text; rie (crash) aplikacji, ze względu na dostęp do zdealo-
// plik implementacji (*.m) kowanych obiektów. Sytuacja taka będzie zauważalna
@synthesize text;
// przypisanie automatycznie zwalnianego obiektu podczas testowania aplikacji na urządzeniu (symulator
// do właściwości spowoduje jego zachowanie nie jest 100% kompatybilny z urządzeniem). Możemy
... również wspomóc się narzędziami instruments (jest to
self.text = [NSString
zestaw narzędzi dostarczanych wraz ze środowiskiem
stringWithString:@"Przykładowy napis"];
... Xcode) do wykrycia wycieków (leaks) lub zdealokowa-
nych obiektów.
Powyżej zostało scharakteryzowanych kilka ważniejszych Listing 18. Instrukcje preprocesora definiujące wspólne
elementów dotyczących zarządzania pamięcią. Zdaję so- wymiary
bie sprawę, iż zabiera to trochę czasu, aby przyzwyczaić
// przydatne marginesy
się do opisanego podejścia i wykorzystywać go. Nie jest #define MARGIN 10
to tak proste, jak automatyczne zarządzanie pamięcią #define BIG_MARGIN 20
w środowiskach, w których działa GC. W iOS 5.0 możemy ...
użyć automatycznego liczenia referencji (ARC), które jest
// deklaracje wspólnych miar
podobne do GC. Ma to również swoje wady, jak i zalety #define CONTROL_WIDTH 200
jak każdy niuans. #define CONTROL_HEIGHT 36
...
Trzeba być bardzo ostrożnym, ponieważ niepoprawna
#define BIG_CONTROL_WIDTH 300
obsługa pamięci powoduje jej wycieki (leaks) lub awa-
// dzięki tym makrom potrzeba tylko jednej linii aby uruchomić alert
#define ALERT_TITLE(X,Y) {UIAlertView *alert = [[UIAlertView alloc] \
initWithTitle:X message:Y delegate:self cancelButtonTitle:@"OK" \
otherButtonTitles: nil];[alert show];[alert release];}
/ www.programistamag.pl / 37
JĘZYKI PROGRAMOWANIA
rozmiar UIView, przesunąć go w inne położenie czy do- kacji w środowisku docelowym. Prawdopodobnie dlate-
pasować do pewnej zawartości. W takich przypadkach za go Apple udostępnił bardzo wierny symulator zachowa-
każdym razem wystarczy wykorzystanie metody setFra- nia iPhone'a. Korzystajmy więc ze wszystkich możliwości
me ustawiającej nową ramkę ze struktury CGRectMake. środowiska i języka, jakie dostarczył nam producent, by
Przedstawione makra na Listingu 20 pomogły mi zaoszczę- tworzyć jak najlepsze oprogramowanie.
dzić wiele cennego czasu. W artykule zostały zaprezentowane elementy języka,
Nie jest to regułą, lecz bardzo dobrym rozwiązaniem które są bardzo przydatne dla każdego developera pra-
jest utworzenie w swoim projekcie dodatkowego pliku za- cującego przy tworzeniu oprogramowania na platformę
wierającego tylko makra. Plik może przyjąć nazwę przy- iOS. W szczególności programowanie z wykorzystaniem
kładowo: NPCommonMacros.h (gdzie: NP=pierwsze litery bloków, implementowanie kategorii, protokoły, selektory,
od nazwy projektu). Możemy wtedy po dołączeniu go do zarządzanie pamięcią oraz użyteczne makra. Wiadomo,
wybranych plików źródłowych wykorzystywać wszystkie iż nie są to wysoce skomplikowane elementy języka, ale
makra, które tylko potrzebujemy w danej sytuacji. warto o nich pamiętać i ich używać w swoich projektach.
Objective-C doczekał się w 2007 roku nowej wersji 2.0
PODSUMOWANIE i ciągle zyskuje na popularności dzięki sukcesom firmy Ap-
ple. Warto więc interesować się tym językiem, a szczegól-
Pisanie oprogramowania na platformy mobilne to wie- nie pewnego jego swoistymi elementami, które mogą nam
le szczegółów i niuansów. To również obszerny temat. pomóc i ułatwić pracę przy rozwijaniu naszych systemów.
Szczegóły te są zauważalne dopiero jak używa się apli-
W SIECI
PP https://developer.apple.com/devcenter/ios/index.action – serwis Apple'a dla developerów iOS, główne trści: iOS Application
Programming Guide, iOS Development Guide – dokumentacje i przewodniki po architekturze iOS. iOS Human Interface Guide-
lines – przewodnik opisujący wytyczne oraz zasady, przydatne podczas projektowania graficznego interfejsu użytkownika dla
aplikacji iOS
PP https://developer.apple.com/technologies/tools/ – opis narzędzi developerskich i zastosowanych w nich rozwiązaniach
PP http://iosdevelopertips.com/ – przydatne porady i fragmenty kodu dla developerów iOS
PP http://stackoverflow.com/ – serwis posiadający ogromne zbiory odpowiedzi (dla większości języków) na problemy powstające
podczas pisania kodu, w szczególności dla Objective-C (http://stackoverflow.com/questions/tagged/objective-c)
Erlang
- język inny niż C++ czy Java
Popularność języków C/C++ oraz Java jak na razie jest niezagrożona, jednakże warto
wiedzieć, że istnieją też inne równie dobre języki programowania. Jednym z nich jest
Erlang, naturalnie daleko mniej popularny niż np: Java. Choć jest to język od Javy
starszy, to jednak pod wieloma względami daleko przewyższa wiele starszych oraz
młodszych języków programowania. W tym krótkim artykule nie sposób opisać ca-
łości języka, jednak mamy nadzieję, że zaprezentowane przykłady zachęcą Czytelni-
ków do dalszych poszukiwań.
B
ez wątpienia Erlang to język inny niż C++ czy Java, aplikacjami Erlanga. Naturalnie Erlang to rozwiązanie
choćby dlatego, iż jego korzenie tego języka sięgają Open Source, ale istnieje możliwość pełnego komercyj-
języka Prolog (programowanie w logice) oraz tech- nego wykorzystania Erlanga, a także uzyskania komer-
nik programowania funkcjonalnego. Paradygmaty progra- cyjnego wsparcia podczas realizacji własnych projektów.
mowania w logice oraz funkcyjnego mocno odróżniają się Możliwe jest to także w Polsce, bowiem istnieje centrum
od programowania sekwencyjnego czy metodologii pro- Erlanga w Krakowie (http://www.erlang-solutions.com/
gramowania obiektowego tak szeroko stosowanych we etc/poland).
współczesnych rozwiązaniach. Jednakże sam Erlang ofe-
ruje także możliwość tworzenia aplikacji rozproszonych INSTALACJA PAKIETU ERLANG
i współbieżnych odpornych na uszkodzenia, co dziś, kiedy
większość systemów działa w środowisku równoległym, Choć język Erlang nie jest tak popularny jak C++ czy
znakomicie się sprawdza i pozwala na tworzenie efektyw- Java, to jednak wszystkie duże dystrybucje Linuxa oferu-
nych rozwiązań. ją gotowe pakiety, więc nie trzeba przeprowadzać własnej
Język Erlang powstał w latach osiemdziesiątych minio-
nego wieku, jego pierwsza wersja została wydana w 1986 Rysunek 1. Strona domowa języka Erlang
roku. Jednak większą popularność zdo-
bywa ostatnio, jako język do budowy
rozproszonych i wydajnych aplikacji.
Warto się zapytać o przykładowe aplika-
cje opracowane w Erlangu, jedną z nich
jest program Wings3D, czyli modeler 3D
cieszący się dużą popularnością. W cało-
ści został opracowany w Erlangu. Dobrym
przykładem jest także serwer Yaws, czy-
li serwer WWW także oparty o Erlanga.
Stosowany jest także na Facebooku.
Generalnie należy powiedzieć, że Er-
lang to język do tworzenia aplikacji cza-
su rzeczywistego, może być stosowany
w bankowości, telefonii, a także jako
język programowania ogólnego. Jedną
z ciekawszych możliwości jest możli-
wość aktywnej podmiany kodu w trakcie
działania aplikacji bez konieczności jej
wyłączania.
Biblioteka języka Erlang oferuje także
zestaw funkcji ułatwiających tworzenie
aplikacji o nazwie OTP. Pakiet ten ułatwia
tworzenie i zarządzanie rozproszonymi
albo
ponownym uruchomieniu musimy skonfigurować środo-
port install erlang wisko startowe Erlanga, co zrobimy, wybierając z Win-
dow, Preferences, Erlang opcję Installed runtimes. Po
W przypadku systemu Windows należy instalator Erlan- tej czynności warto raz jeszcze zamknąć i ponownie uru-
ga ściągnąć ze strony domowej. Najnowsza wersja to chomić Eclipse i można zakładać nasz pierwszy projekt.
R15B01, więc będzie to plik o nazwie otp_win32_R15B01. Trzeba jeszcze dodać, iż Erlide pozwala na konfigurację
exe. Po jego ściągnięciu i instalacji otrzymujemy kom- procesu uruchamiania projektu, możemy więc wskazać
pletne środowisko do pracy z językiem Erlang. Obecny moduł główny i predykat startowy, co pozwoli na wygod-
jest także pakiet wxErlang, czyli biblioteka do obsługi ne testowanie aplikacji.
GUI. W przypadku instalacji Erlang, w systemach Linux
czy MacOS, warto upewnić się, czy pakiet wxErlang został PODSTAWY ERLANGA – TYPY
zainstalowany, np. dla MacPorts należy wydać polecenie DANYCH
o postaci:
Programy w języku Erlang mają zupełnie inną składnię
port install erlang +wxwidgets niż programy tworzone w typowych językach programo-
wania jak Java czy C++. Wynika to naturalnie z faktu, iż
aby zainstalować obsługę dla GUI. Może też się okazać, Erlang to język oparty o język Prolog, gdzie dodatkowo
że trzeba będzie samodzielnie przeprowadzić kompilację szeroko są wykorzystywane funkcyjne techniki progra-
Erlanga. Dokładne informacje jak to zrobić można odszu- mowania. Oznacza to, iż będzie trzeba zmienić sposób
kać w dokumentacji. myślenia o programach, jakie się tworzy w Erlangu, a to
Ponieważ praca z poziomu konsoli z pewnością nie jest nie jest wbrew pozorom zadanie łatwe, szczególnie jeśli
przyjazna dla użytkownika początkującego, to warto za- długo pisaliśmy programy w tradycyjnych językach struk-
tem postarać się o instalację środowiska Eclipse oraz od- turalnych i obiektowych.
powiedniej wtyczki (o nazwie Erlide), która ułatwi naukę Erlang jednak posiada rozbudowany aparat do tworze-
i tworzenie pierwszych programów w Erlangu. nia tzw. programów sekwencyjnych, a więc podobnych co
Jeśli dokonaliśmy instalacji Erlang'a np. w środowisku do idei do programów pisanych w Javie czy C++, co tylko
Windows (należy zwrócić uwagę, aby poszczególne na- ułatwia tworzenie własnych programów.
zwy katalogów nie zawierały spacji, z tego też powodu Do podstawowych pojęć występujących w Erlangu na-
lepiej nie instalować Erlanga do katalogu Program Files), leżą oczywiście liczby całkowite, liczby zmiennoprzecin-
to instalacja jest bardzo prosta, sprowadza się do urucho- kowe, przykładowo:
mienia środowiska Eclipse, następnie z menu Help wybie-
ramy opcję Install new software..., w oknie dialogowym 12, -234, 16#AB5E, 2#1001010, $A, 3.14, 3.34E-5
wpisujemy adres http://erlide.org/update, a następnie
wybieramy moduły Erlanga do instalacji, a po kilku chwi- Należy dodać, że np. 2#1001010 to zapis liczby w postaci
lach zgadzamy się na restart środowiska Eclipse. Po dwójkowej, ogólnie można bowiem pisać B#VAL, gdzie B
/ www.programistamag.pl / 41
JĘZYKI PROGRAMOWANIA
Listing 1 zawiera nasz pierwszy program w Erlangu, choć Po kompilacji zobaczymy komunikat ok, i możemy spraw-
tak naprawdę nie jest to pełny program, a jedynie zbiór dzić działanie np. funkcji silnia:
predykatów (nazywanych także procedurami), czyli tzw.
moduł. Pierwsza linia to nazwa modułu; powinna być taka przyklad1:silnia(10).
sama jak nazwa pliku. Następnie słowem export określamy
listę symboli publicznych, widocznych dla innych użytkowni- Listing 1 zawiera także predykat pole do obliczenia pola
ków naszego modułu. Zapis silnia/1 oznacza, że funkcja sil- powierzchni kwadratu oraz koła. Obliczenie pola kwadratu
nia z jednym argumentem będzie udostępniona publicznie. jest realizowane w następujący sposób:
pole(Other) ->
{invalid_object, Other}. Poszczególne etapy tego procesu można zaobserwować
ponownie na Rysunku 3. Istnieje też możliwość odczyta-
nia dwóch elementów z początku, np. pisząc:
GŁOWA I OGON, CZYLI LISTA
[A,B|C]=[1, 2, 3, 4, 5].
/ www.programistamag.pl / 43
JĘZYKI PROGRAMOWANIA
Algorytm sortowania szybkiego zawsze jest podawany jako przykład zawartości opisu algorytmu w językach funkcyjnych.
W przypadku Erlanga jest podobnie, co potwierdza poniższy kod:
-module(przyklad2).
-export([qsort/1]).
qsort([Pivot|Rest]) ->
{Smaller, Larger} = partition(Pivot, Rest,[],[]),
qsort(Smaller) ++ [Pivot] ++ qsort(Larger).
Idea programu jest identyczna do opisu sortowania szybkiego, wybiera się pewien element (tzw. pivot), a następnie przerzuca
się elementy większe i mniejsze przed lub za tym wybranym elementem. W Erlangu wykorzystujemy naturalnie listy i operację
podziału na głowę i ogon. Przemieszczenie elementów jest realizowane przez procedurę partition, natomiast qsort dzieli, a na
stępnie łączy posortowane fragmenty listy.
W przypadku naszego kalkulatora poszczególne wywoła- wysłaniu komunikatu za pomocą receive czekamy na ko-
nia calc modyfikują listę z obliczeniami. munikat zwrotny w tym samym formacie {From, Msg},
a po jego otrzymaniu za pomocą io:format wyświetlamy
Listing 2. Kalkulator dla odwrotnej notacji polskiej na konsoli prosty komunikat. Jednak nasz proces z pro-
cedurą loop nadal jest aktywny, możemy go wyłączyć,
-module(rpn). przekazując komunikat stop.
-export([calc/1]).
Jak widać, jest to bardzo prosty przykład, jednakże jest
calc(L) when is_list(L) -> to już aplikacja rozproszona. Możemy, do czego zachęcam,
[R] = lists:foldl(fun calc/2, [],
moduł loop umieścić w innym module, a procedura start
string:tokens(L, " ")), R.
nadal będzie posiadać podobną postać, przy czym spawn
calc("+", [L1,L2|R]) -> [L2+L1|R]; będzie naturalnie odnosił się do innego modułu. W nieco
calc("-", [L1,L2|R]) -> [L2-L1|R];
calc("*", [L1,L2|R]) -> [L2*L1|R]; inny sposób będzie trzeba utworzyć nowy proces.
calc("/", [L1,L2|R]) -> [L2/L1|R];
calc("^", [L1,L2|R]) -> [math:pow(L2,L1)|R]; Listing 3. Przekazywanie komunikatów
calc("sin", [L1|R]) -> [math:sin(L1)|R];
calc(L, R) -> [read(L)|R].
-module(echo).
read(L) -> -export([start/0, loop/0]).
case string:to_float(L) of
{error,no_float} -> list_to_integer(L); start() ->
{F,_} -> F Pid = spawn(echo, loop, []),
end. Pid ! {self(), hello},
receive
{Pid, Msg} -> io:format("echo: ~w~n",[Msg])
NADAWCY I ODBIORCY WIADOMOŚCI end,
Pid ! stop.
a procedura loop jest ponownie, dzięki rekurencji, uru- zgodnie z oczekiwaniami udostępnia wszystkie procedury
chamiana. Drugi komunikat stop, zgodnie ze swoją na- znajdujące się w module, natomiast:
zwą, zatrzyma działanie loop, ponieważ nie pojawia się
kolejne rekurencyjne wywołanie tej pętli. -include_lib("wx/include/wx.hrl").
Nic więcej o procedurze loop nie da się powiedzieć,
toteż warto wrócić do start. Po uruchomieniu procesu za dołącza definicje różnego rodzaju stałych przydatnych
pomocą spawn, otrzymaliśmy jego identyfikator. Możemy podczas tworzenia programów korzystających z GUI.
teraz, wykorzystując operator !, przesłać komunikat np: Mamy jeszcze definicje stałych:
/ www.programistamag.pl / 45
JĘZYKI PROGRAMOWANIA
Stałe te będą przez nas wykorzystywane podczas wykry- Str ="Prosty przykład w wxErlang.",
wania zdarzeń i tworzenia widgetów. Program z Listingu 4 MD = wxMessageDialog:new(Frame, Str,
[{style, ?wxOK bor ?wxICON_INFORMATION},
ma trzy procedury. Pierwsza start uruchamia naszą apli- {caption, "O Programie"}]),
kację, setup tworzy interfejs, natomiast loop jest odpo- wxDialog:showModal(MD),
wiedzialna za przetwarzanie komunikatów. wxDialog:destroy(MD),
W całym procesie tworzenia menu nie wykorzystujemy
żadnych specjalnych technik programowania, utworzenie Powyższe linie to typowy kod sekwencyjny, co pozwala
menu Plik, które zawiera jedną opcję Plik, to następujące na łatwe wdrożenie się do pisania programów w Erlangu,
polecenia: gdyż możemy tworzyć oprogramowanie przynajmniej na
początku w sposób dobrze znany, bez konieczności stoso-
MenuBar = wxMenuBar:new(), wania rozwiązań wyłącznie funkcyjnych bądź też progra-
File = wxMenu:new(),
wxMenuBar:append(MenuBar, File, "&Plik"),
mowania w logice.
Nie trzeba też okien mozolnie projektować bezpo-
Poszczególne polecenia rozdzielamy przecinkiem, trzeba średnio poprzez tworzenie kodu, możemy utworzyć po-
zwrócić uwagę na nakazanie obsługi komunikatów, co ro- trzebne elementy interfejsu w postaci plików w formacie
bimy w następujący sposób: XRC. Pakiet wxErlang obsługuje pliki XRC, więc warto
skorzystać z tej możliwości, oszczędność czasu będzie
wxFrame:connect(Frame, command_menu_selected), znacząca.
wxFrame:connect(Frame, close_window).
Help = wxMenu:new(),
Jednakże niezbędne jest wprowadzenie jednej zmiany
wxMenu:append(Help, ?ABOUT, "O programie"), w pliku talker.erl. Należy go dopasować do używanej
wxMenu:append(File, ?EXIT, "Koniec"),
przez nas sieci, a dokładniej zmienić nazwę hosta na od-
wxMenuBar:append(MenuBar, File, "&Plik"), powiedni. Jeśli nasz host nosi nazwę pc0, a węzeł Erlanga,
wxMenuBar:append(MenuBar, Help, "P&omoc"), na którym zostanie uruchomiony serwer, to w0, to pro-
wxFrame:setMenuBar(Frame, MenuBar),
cedura server_node_name posiada następującą postać:
/ www.programistamag.pl / 47
JĘZYKI PROGRAMOWANIA
cji może odbywać się także w rzeczywistym środowisku mnieć, jest rozwiązaniem, które łatwo łączy się z innymi
sieciowym. językami, np. C/C++. Dlatego jeśli planujemy tworzyć
serwer działający w ramach naszej platformy, to może
PODSUMOWANIE warto rozważyć zastosowanie Erlanga, który znakomicie
nadaje się do tworzenia serwerów ze względu na obec-
Na zakończenie warto jak zawsze zachęcać do dalszych ność systemu komunikatów. Choć tak naprawdę jest to
poszukiwań, wbrew pozorom o Erlangu znajdziemy sporo język uniwersalny i można w nim tworzyć wydajne aplika-
informacji, także różnego rodzaju przykłady, więc warto cje rozwiązujące różnego rodzaju problemy. Wydaje się,
poszukać. Trzeba także wspomnieć o dwóch książkach: iż jest również łatwiejszy do stosowania niż np. Prolog,
pierwsza to „Erlang Programming” oraz druga „Erlang and ze względu na dużą liczbę typowych konstrukcji sekwen-
OTP in Action”, które stanowią podręczniki dla tego języ- cyjnych, co tylko ułatwia tworzenie pierwszych własnych
ka programowania. Erlang, o czym nie było okazji wspo- programów w Erlangu.
/ www.programistamag.pl / 49
PROGRAMOWANIE GRAFIKI
Wojciech Sura
Direct3D – podstawy
Użytkownicy komputerów często nie mają pojęcia, jak wielka moc obliczeniowa
drzemie w ich karcie graficznej. Współczesne procesory mają zwykle dwa lub cztery
rdzenie, gdy tymczasem liczba rdzeni przeciętnej karty graficznej przekracza już licz-
bę 200. Ujarzmienie takiej potęgi daje programiście bardzo dużo możliwości – i co
ciekawe, wcale nie jest takie trudne.
N
a początku zadajmy sobie proste pytanie: czy na trójkąt. Nie ma mowy o czymś takim, jak bryła, punkt
ekranie komputera można wyświetlić trójwymia- zaczepienia, kamera czy światło. Każdy z tych elemen-
rową grafikę? Mnogość gier komputerowych i pro- tów musi zostać - w oparciu o mechanizmy Direct3D
gramów typu CAD sugerowałaby odpowiedź twierdzącą, - zaimplementowany przez programistę.
a przecież jest dokładnie na odwrót: ekran komputera Przyjrzyjmy się więc, jak – z perspektywy programisty
może wyświetlić tylko płaskie obrazy. Innymi słowy to, co – wygląda proces renderowania grafiki 3D.
widzimy na ekranie, jest tylko dwuwymiarową projekcją
pewnej trójwymiarowej sceny. Właściwą analogią może RENDERING PIPELINE
być zdjęcie lub film, które utrwalają trójwymiarowy świat
na dwuwymiarowym medium. Proces renderowania podzielony jest na etapy, ułożone
Wykonanie takiej projekcji nie jest, wbrew pozorom, wzdłuż specyficznej ścieżki, nazywanej rendering pipeli-
aż tak trudne - dosyć powiedzieć, że już w latach 80' po- ne. Niektóre etapy realizowane są w całości przez kartę
wstawały gry na Atari, które potrafiły w czasie rzeczywi- graficzną, inne mogą być częściowo kontrolowane przez
stym zobrazować pewien prymitywny, ale już trójwymia- programistę, zaś jeszcze inne muszą zostać przygotowa-
rowy świat. Jednak w przypadku bardziej skomplikowanej ne przez niego w całości. Ponadto, niektóre elementy tej
i realistycznej grafiki rośnie znacząco złożoność algoryt- ścieżki są opcjonalne, zaś obecność innych jest wyma-
mów ją renderujących, co dramatycznie odbija się na ich gana do prawidłowego przeprowadzenia całego procesu.
wydajności. Osiągnięcie kilkudziesięciu klatek na sekun- Rysunek 1 przedstawia najprostszą ścieżkę zawierającą
dę dla sceny składającej się z tysięcy trójkątów wypeł- wszystkie elementy wymagane do wyrenderowania sceny
nianych setkami megabajtów tekstur tylko przy pomocy zawierającej trójwymiarowe bryły. Proces renderowania
procesora (nie zapominając o logice gry) jest nierealne.
Aby rozwiązać ten problem, powstały dedykowane układy Rysunek 1. Ścieżka renderowania
wspomagające renderowanie grafiki, montowane na kar-
tach graficznych. W ten sposób karta, która pierwotnie
służyła tylko do wyświetlania obrazu na ekranie, stała się
w pewnym momencie niewielkim centrum obliczeniowym
zdolnym wykonywać ogromne ilości operacji zmienno-
przecinkowych w czasie rzeczywistym.
Architektura jednostek obliczeniowych karty graficz-
nej rożni się znacząco od architektury procesora. Aby nie
zmuszać programisty do uczenia się programowania na
zupełnie nową platformę i jednocześnie by standaryzo-
wać dostęp do mocy obliczeniowej karty graficznej, po-
wstały specjalne API - pomosty, pośredniczące pomiędzy
aplikacją a sterownikiem i umożliwiające wykorzystanie
możliwości karty podczas programowania w języku wyż-
szego poziomu. Jednym z takich pomostów jest właśnie
Direct3D, składnik biblioteki DirectX rozwijanej przez
Microsoft.
Z perspektywy programisty, Direct3D jest rendererem,
czyli mechanizmem odpowiedzialnym za przetworzenie
i wyświetlenie na ekranie przekazanych mu danych. API
Direct3D jest stosunkowo niskopoziomowe: dosyć za-
uważyć, ze najbardziej zaawansowanym obiektem, z któ-
rym mamy do czynienia podczas pracy z tą biblioteką jest
Input assembly
/ www.programistamag.pl / 51
PROGRAMOWANIE GRAFIKI
/ www.programistamag.pl / 53
PROGRAMOWANIE GRAFIKI
reklama
W
ramach artykułu wskażemy, w jaki sposób uzy- ■■ strumień głębi – pozwala pobrać informacje o odległo-
skać dostęp do strumieni danych przesyłanych ści obiektów od sensora; Kinect identyfikować może te
przez sensor; nabytą wiedzę wykorzystamy do znajdujące się w odległości od ok. 800 mm do 4000
zaimplementowania prostego programu analizującego mm (w wersji dla Windows – od ok. 400 mm, z pewny-
gesty oraz mowę użytkownika i pokażemy, jak wyko- mi ograniczeniami),
rzystać go do sterowania aplikacjami uruchomionymi na ■■ strumień danych szkieletowych – transmituje infor-
komputerze – takimi, jak przeglądarka zdjęć czy program macje o położeniu wyróżnionych elementów ciała (jo-
prezentacyjny. ints w terminologii Kinect SDK) maksymalnie dwóch
Wcześniej jednak wskażemy na możliwości techniczne osób (śledzenie aktywne). Elementy te przedstawia
urządzenia. rysunek 2. Dodatkowo sensor może udostępnić dane
o położeniu sylwetek czterech dodatkowych osób (bez
BUDOWA I MOŻLIWOŚCI SENSORA szczegółowych informacji o położeniu poszczególnych
joints – śledzenie pasywne). Funkcjonalność ta zwalnia
Generalnie w sensorze wyróżnić można następujące ele- programistę z konieczności implementacji algorytmów
menty (por. rysunek 1): wykrywających, na podstawie strumieni wideo i głębi,
sylwetkę ludzką i dokonywania analizy jej położenia.
1. Czteroelementowy zestaw mikrofonów – umożliwia Pozwala to skupić się na implementacji rozwiązań re-
rejestrację dźwięku i lokalizację jego źródła w prze- alizujących docelową funkcjonalność tworzonego przez
strzeni; mikrofony wyposażone są w funkcję redukcji programistę oprogramowania,
hałasu. ■■ strumień danych audio – umożliwiający redukcję ha-
2. Emiter podczerwieni – generujący wiązkę promieni łasu, analizę źródła dźwięku i rozpoznawanie mowy
podczerwonych, wykorzystywanych przez kamerę głę- (wraz z Microsoft Speech SDK).
bokości (4) do pomiaru odległości obiektów znajdują-
cych się w polu widzenia sensora. INSTALACJA SDK I KONFIGURACJA
3. Kamera rejestrująca strumień wideo. ŚRODOWISKA
4. Kamera głębokości, opisana wyżej w pkt. 2.
Przejdziemy teraz do praktycznej części artykułu. Czytel-
Wskazane wyżej możliwości techniczne powodują, iż nicy chcący wypróbować prezentowane programy powin-
programista ma dostęp do następujących strumieni ni (oprócz oczywiście posiadania sensora Kinect – czy to
danych: w wersji dla Xbox-a, czy to dla Windowsa) zainstalować
SDK, dostępne na stronie http://kinectforwindows.org.
■■ strumień wideo – w rozdzielczościach 640x480 (RGB Wykorzystanie SDK wymaga odpowiedniego skonfigu-
i YUV) i 1280x960 (wyłącznie RGB), rowania środowiska programistycznego. Zadbać należy
Rysunek 2.
Śledzone elementy (źródło: http://i.msdn.microsoft.com/dynimg/
Rysunek 4. Pierwszy program w działaniu IC539011.png)
Interfejs wykorzystuje bibliotekę Windows Forms i składa danych. Obsługę strumienia wideo prezentuje Listing 2
się z pojedynczej formy wraz z kontrolką PictureBox, któ- (str. 59).
rą nazwiemy videoStream. Na początku otwieramy ramkę wideo i sprawdzamy,
Kod inicjujący w naszym wypadku wyglądał będzie tak, czy nie jest pusta. W wypadku bowiem, gdyby kod obsłu-
jak pokazano w Listingu 1 (str. 59). gujący zdarzenie działał zbyt powoli i nie nadążał z sukce-
Rozpoczynamy od pobrania obiektu reprezentującego sywną obsługą kolejnych zgłoszeń, zmienna image miała-
sensor – z kolekcji KinectSensors, statycznego pola kla- by wartość null.
sy KinectSensor, reprezentującego wszystkie urządzenia Następnie tworzymy bufor, w którym zapisane zostaną
Kinect dostępne w systemie. Następnie inicjujemy stru- dane pochodzące ze strumienia wideo – i wypełniamy go.
mień danych wideo (ColorStream), a także dodajemy ob- W dalszej części konwertujemy „surowe” dane pochodzą-
sługę zdarzenia AllFramesReady – wywoływanego, gdy ce z sensora do postaci bitmapy colorBitmap, odwracamy
dostępna jest nowa ramka danych. Możliwe byłoby rów- (dane stanowią „lustrzane odbicie” rzeczywistych) i wy-
nież obsłużenie zdarzenia ColorFrameReady, związanego świetlamy w kontrolce videoStream.
wyłącznie ze strumieniem danych wideo.
Rozwiązanie oparte na zdarzeniach zalecane jest jako DRUGI PROGRAM – STRUMIEŃ GŁĘBI
zapewniające największą responsywność aplikacji; alter- I DANYCH SZKIELETOWYCH
natywnie możliwe jest odpytywanie w pętli o zawartość
kolejnych ramek, korzystając z wywołań w rodzaju ki- W następnym przykładowym programie pokażemy,
nect.ColorStream.OpenNextFrame(1000) – gdzie jako w jaki sposób uzyskać dostęp do danych z czujnika głę-
argument podajemy czas oczekiwania na nowy zestaw bi. GUI będzie analogiczne do poprzedniego i składać
/ www.programistamag.pl / 57
PROGRAMOWANIE URZĄDZEŃ
się będzie z pojedynczej kontrolki PixtureBox o nazwie Przystąpimy teraz do naniesienia na bitmapę punktów
depthStream: położenia joints. Służy do tego następujący kod (zobacz
str. 60).
Otwieramy ramkę danych szkieletowych i również
sprawdzamy, czy nie jest pusta. Podobnie jak wyżej,
dane zapisane w ramce kopiujemy do bufora skeletons
– i iterujemy po zapisanych danych szkieletowych użyt-
kowników śledzonych aktywnie (w wypadku których pole
TrackingState ma wartość SkeletonTrackingState.
Tracked). Zaznaczenie pozycji każdego joint na ekranie
wymaga skonwertowania pozycji ze współrzędnych 3D
(w ten sposób ich pozycja identyfikowana jest w strumie-
niu danych szkieletowych) na dwuwymiarowe współrzęd-
ne bitmapy. Służy do tego metoda MapSkeletonPoint-
ToDepth klasy KinectSensor. Wypisujemy pozycje tych
tylko joints, które znajdują się w polu widzenia sensora
Rysunek 5. Drugi program w działaniu i są przezeń śledzone (pole TrackingState o wartości Jo-
intTrackingState.Tracked).
Obiekt będzie tym jaśniejszy, im bardziej oddalony będzie Wreszcie, w ostaniej linii, (odwrócona) bitmapa wy-
od sensora. Sylwetki użytkowników oznaczane będą kolo- świetlana jest w kontrolce PictureBox.
rem, tło zaś prezentowane będzie w odcieniach szarości. Dla porządku należy dodać, że obsługa zdarzeń
Dodatkowo – zaznaczone zostaną punkty ciała (joints) w osobnym wątku wykonania znacznie podniosła wydaj-
śledzone przez sensor. ność aplikacji na komputerze, na którym była testowana.
Kod inicjujący przedstawia się analogicznie do tego Nie stanowi to jednak istoty niniejszego artykułu, dlate-
z pierwszego programu (Listing 3). go też część aplikacji związana z obsługą wątków została
Włączenie strumienia danych szkieletowych pozwoli pominięta.
nie tylko zaznaczać śledzone punkty ciała, ale również
identyfikować sylwetki użytkowników w strumieniu głębi, TRZECI PROGRAM –
a co za tym idzie – odpowiednio je kolorować. ROZPOZNAWANIE GESTÓW I MOWY
Kod obsługujący zdarzenie, dla jasności, podzielono na
dwie logiczne części, związane z obsługą odpowiedniego Wykorzystując powyższą wiedzę, przejść możemy do
strumienia. Zacznijmy od strumienia głębi. implementacji zapowiedzianego na wstępie programu
Ze względu na konieczność zdekodowania danych, kod rozpoznającego gesty. Niniejszy program ma charakter
jest nieco bardziej skomplikowany niż ten dla strumienia przykładowy, toteż jego funkcjonalność będzie nieco
wideo. Wygląda w sposób następujący (Listing 4). ograniczona: śledzić będziemy gesty wykonywane przez
Na początku wprowadzamy siedem barw bazowych, prawą rękę; aplikacja rozpozna gest polegający na jej
identyfikujących tło i sześciu użytkowników. Następnie przesunięciu w lewo i prawo (podobne gesty rozpoznawa-
definiujemy bitmapę, którą wyświetlać będziemy w kon- ne są w interfejsach telefonów komórkowych jako „dalej”
trolce depthStream GUI programu, a także bufor na i „wstecz”). Aby zbytnio nie komplikować kodu źródłowe-
strumień danych głębi. Pobieramy także informacje go, zakładamy, że z sensora korzystać będzie pojedynczy
o zakresie, w którym sensor jest w stanie identyfikować użytkownik, który nie będzie opuszczał jego pola widze-
oddalenie obiektów. W wypadku Kinecta konsoli Xbox nia. Dla prostoty pominiemy również kwestie związane
uzyskano przedział od 800 mm do 4000 mm. Następnie z architekturą aplikacji rozpoznającej gesty.
otwieramy strumień i kopiujemy dane do bufora. Czytelnik nie powinien mieć problemów z ominięciem
W tym miejscu kod nieco się komplikuje. Nie może- tych ograniczeń w swojej aplikacji. Bardziej rozbudowa-
my, jak w poprzednim programie, po prostu skopiować ne przykłady w tym względzie znaleźć można w Interne-
danych z bufora do bitmapy, lecz musimy poddać je ob- cie – por. wykaz adresów na końcu niniejszego artykułu.
róbce. Każdy „piksel” danych strumienia głębi to poje- W tym wypadku inicjować musimy jedynie strumień
dyncza dana typu short. Jej pierwszych 13 bitów zawie- danych szkieletowych (Listing 6, str. 60).
ra informacje o oddaleniu (w mm) punktu od sensora, Rozpoznawanie gestów założonych na wstępie niniej-
a ostatnie 3 – identyfikują piksele, które składają się na szego artykułu nie jest trudne. Dla zbadania, czy ręka
sylwetkę użytkownika (każdy użytkownik ma przyznaną wykonała gest „dalej”, wystarczy sprawdzić, czy w ciągu
liczbę od 1 do 7; 0 przeznaczone jest dla tła). Informacje ostatnich dwóch sekund spełniono łącznie następujące
te wydobywamy w pętli i przechowujemy w zmiennych warunki:
distance i idx. Następnie sprawdzamy, czy odczytana
odległość zawiera się w zakresie śledzenia sensora i jeśli ■■ użytkownik przesuwał dłoń wyłącznie w prawo,
tak – odpowiadający piksel wyjściowej bitmapy „cieniuje- ■■ na odległość co najmniej 40 cm,
my” odpowiednią barwą. ■■ dłoń pozostawała stabilna w poziomie,
kinect = KinectSensor.KinectSensors[0];
kinect.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);
kinect.SkeletonStream.Enable();
kinect.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(kinect_AllFramesReady);
kinect.Start();
/ www.programistamag.pl / 59
PROGRAMOWANIE URZĄDZEŃ
kinect = KinectSensor.KinectSensors[0];
kinect.SkeletonStream.Enable();
kinect.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(kinect_SkeletonFrameReady);
kinect.Start();
class Positions
{
public SkeletonPoint Hand { get; set; }
public SkeletonPoint HipCenter { get; set; }
public SkeletonPoint ShoulderCenter { get; set; }
public DateTime Time { get; set; }
}
if (IsSwypeRight())
{
_positions.Clear();
}
if (IsSwypeLeft())
{
_positions.Clear();
}
■■ przemieszczała się wyłącznie dłoń użytkownika; reszta cy i górnej części tułowia – w celu sprawdzenia założenia
sylwetki pozostawała (względnie) nieruchomo. o stabilności całej sylwetki.
W funkcji obsługi zdarzenia wybieramy (jedyną) śle-
W celu ich weryfikacji w programie rejestrować będziemy dzoną aktywnie sylwetkę i zapisujemy rejestrowane in-
dane z ostatnich 90 ramek danych szkieletowych. Prze- formacje w liście _positions (Listing 8).
chowamy je w następującej klasie (Listing 7). Sprawdzenie, czy wykonano gest, dokonywane jest
Zapisujemy więc informacje o położeniu dłoni i reje- w metodach IsSwypeRight oraz IsSwypeLeft. Pierwsza
strujemy czas, w którym położenie to zostało odczytane. z nich prezentuje się następująco (Listing 9).
Dodatkowo przechowujemy informacje o pozycji miedni- W powyższym kodzie za pomocą komentarzy oznaczo-
if (IsSwypeRight())
{
_positions.Clear();
SendKeys.Send("{RIGHT}");
}
if (IsSwypeLeft())
{
_positions.Clear();
SendKeys.Send("{LEFT}");
}
engine.RecognizeAsync(RecognizeMode.Multiple);
/ www.programistamag.pl / 61
PROGRAMOWANIE URZĄDZEŃ
no miejsca, gdzie badane są wskazane na wstępie warun- W pierwszej kolejności przygotowujemy silnik rozpo-
ki co do pozycji poszczególnych joints: znawania mowy, opierając się na tym zainstalowanym już
w systemie, dostępnym dla języka angielskiego (polski
1. badamy ramki z ostatnich 2 sekund, nie jest niestety dostępny), identyfikowanym przez ciąg
2. sprawdzamy, czy użytkownik przemieszczał prawą dłoń znaków „SR_MS_en-US_Kinect_11.0”. Następnie przygo-
wyłącznie w prawo, towujemy strukturę gramatyczną rozpoznawanych przez
3. sprawdzamy, czy dłoń pozostała stabilna w poziomie, sensor poleceń. Wskazujemy, iż składać się one będą ze
tj. nie przemieściła się o więcej niż o 10 cm, słowa „rotate” oraz kierunku tego obrotu – alternatywnie
4. sprawdzamy stabilność położenia punktu odpowiada- „left” lub „right”. Obsługa zdarzenia polegającego na
jącego miednicy – również, czy nie przemieściła się rozpoznaniu polecenia głosowego miała będzie miejsce
o więcej, niż o 10 cm, w metodzie engine_SpeechRecognized (Listing 12, po-
5. badamy stabilność górnej części tułowia, analogicznie przednia strona).
do położenia miednicy, Badamy najpierw poziom pewności, że dane polecenie
6. ostatecznie: sprawdzamy, czy dłoń przesunięto na od- głosowe zostało w istocie rozpoznane. Jeżeli przekracza
ległość co najmniej 40 cm. on – dobrany doświadczalnie – poziom 80%, to przesyła-
my do systemu odpowiedni kod odpowiedzialny za obrót
Łączne spełnienie wszystkich powyższych warunków obrazka, uzależniony od wskazanego przez użytkownika
spowoduje, iż odczytany zostanie gest przesunięcia dło- kierunku.
ni w prawo. Wszelkie wartości liczbowe powyżej zostały Pozostaje jeszcze skonfigurowanie biblioteki Micro-
dobrane eksperymentalnie. Kod dla gestu przesunięcia soft Speech do współpracy z sensorem – wskazanie, że
w lewo będzie, oczywiście, analogiczny. źródłem rozpoznawanego dźwięku jest strumień audio
Pozostało jedynie wskazać, w jaki sposób wykorzystać Kinecta.
odczytane gesty. Proponuję zmodyfikować końcową część Uruchomienie strumienia następuje poprzez wywołanie
funkcji obsługi zdarzenia SkeletonFrameReady w nastę- odpowiedniej metody: kinect.AudioSource.Start();
pujący sposób (Listing 10, poprzednia strona). powinno ono nastąpić co najmniej cztery sekundy po
Spowoduje to wysłanie do aktywnego okna zdarze- wywołaniu metody kinect.Start() (por. np. Listing
nia odpowiadającego za naciśnięcie lewego lub prawego 1) – dopiero bowiem po takim czasie Kinect gotowy jest
klawisza kursora. Jeżeli aktywną aplikacją byłaby prze- przesyłać dane audio. Następnie strumień ten wskazuje-
glądarka zdjęć, spowodowałoby to zmianę oglądanej fo- my jako źródłowy dla biblioteki Microsoft Speech i inicju-
tografii; w wypadku aktywnego PowerPoint’a – zmianę jemy (asynchroniczne) rozpoznawanie mowy (Listing 13,
aktualnego slajdu. poprzednia strona).
Przykład ten rozbudujemy teraz o możliwość rozpo- Warto zauważyć, że – podobnie j/w – uruchomienie
znawania mowy – dodamy możliwość obracania aktualnie rozpoznawania mowy w osobnym wątku wykonania zna
oglądanego w przeglądarce zdjęć obrazka poprzez wyda- cznie podnosi efektywność całej aplikacji.
nie głosowego polecenia „rotate left/right”.
W tym celu skorzystamy z biblioteki Microsoft Speech ZAKOŃCZENIE
Platform SDK w wersji 11, dostępnej bez opłat na stro-
nie firmy Microsoft. W tym miejscu należy zaznaczyć, że W artykule przedstawiono sposób uzyskania dostępu do
większość źródeł wskazuje na konieczność skorzystania strumieni danych udostępnionych przez SDK. Czytelników
z 32-bitowej wersji SDK; następujący przykład z powo- chcących pozyskać więcej informacji zachęcam do odwie-
dzeniem jednak współpracuje z jego 64-bitową odmianą. dzenia adresów internetowych wymienionych w ramce
Po zainstalowaniu biblioteki należy dodać do projek- „W Sieci”. Polecam również dalsze eksperymentowanie
tu odpowiednią referencję, analogicznie jak w wypadku z urządzeniem.
SDK Kinecta. Odpowiedni plik DLL domyślnie dostępny
jest w następującej lokalizacji: C:\Program Files\Micro-
„W SIECI”:
soft SDKs\Speech\v11.0\Assembly\Microsoft.Speech.dll.
Z biblioteki korzysta następujący kod (Listing 11, po- PP http://kinectforwindows.org
przednia strona). PP http://msdn.microsoft.com/pl-pl/library/hh428637.aspx
PP http://kinecttoolbox.codeplex.com/
Prenumerata - korzyści:
»» Prenumerata magazynu Programista to wygodny sposób na dostęp do aktualnej i przydatnej wiedzy dla specjalistów IT.
»» Nie wychodząc z domu będziesz otrzymywał czasopismo tradycyjną pocztą lub dostaniesz e-mail z informacją o
nowym wydaniu do pobrania.
»» Cena egzemplarza w prenumeracie jest niższa względem pojedyńczego numeru.
»» Prenumerując magazyn Programista zyskujesz czas i pieniądze.
ZAMAWIAM
CZAS TRWANIA PRENUMERATĘ OD
RODZAJ PRENUMERATY PRENUMERATY CENA BRUTTO NUMERU:
Wydanie drukowane Roczna 12 wydań 199,00 zł + 36 zł przesyłka*
Wydanie drukowane Dwuletnia 24 wydania 398,00 zł + 72 zł przesyłka*
Wydanie elektroniczne Roczna 12 wydań 99,00 zł
Wydanie elektroniczne Dwuletnia 24 wydania 198,00 zł
Dane zamawiającego i Imię i nazwisko:
adres wysyłki
Adres wysyłki:
e-mail:
Telefon:
Dla firm (faktura) Nazwa firmy:
Adres siedziby firmy:
NIP:
Prenumerata zostanie uruchomiona po zaksięgowaniu wpłaty na konto wydawcy:
Anna Adamczyk, ul.Dereniowa 4/47, 02-776 Warszawa,
Bank Pekao S.A. 34 1240 1125 1111 0010 2401 3333
że w innym kontekście podsystem komentarzy może być Modele odseparowane przez Bounded Context nie
krytyczny (ponieważ np. buduje zaufania) – wówczas mogą odnosić się do siebie wprost. Są to niezależne byty,
traktujemy go jako Core Domain. które mogą być rozwijane przez osobne zespoły. Oma-
wiane w dalszej części Wzorce Architektoniczne pozwa-
Generic domain – modele domen bazowych lają na hermetyzację modeli, która redukuje problemy
związane ze zmianami i pozwala na niezależne rozwijanie
Pewne domeny są generyczne w stosunku do naszej głów- modeli przez zespoły, które nie „wchodzą sobie w drogę”.
nej domeny. Przykładowo tworząc system służący do han- Projekt referencyjny (link w ramce „w sieci”) zawiera
dlu zawartością multimedialną, nie będziemy skupiać wy- 3 przykładowe Bounded Context: Sales, CRM, Shipping.
siłku mentalnego, pracując nad modułem fakturowania. W przypadku tego projektu BC odpowiadają modułom
Fakturowanie jest krytyczne (zależy nam na jakości tego systemu.
modułu), ale przede wszystkim nie stanowi ono głównej
wartości, oraz ponadto tego typu systemy istnieją na ryn- Granice kompetencji Ekspertów domenowych
ku (zarówno Open Source, jak i komercyjnym). Dlatego
też przykładowe fakturowanie możemy potraktować jako W systemach o większej skali mamy często do czy-
Generic Domain. Z podsystemami zaliczanymi do Generic nienia z sytuacją, gdzie korzystamy z wiedzy wie-
Domains integrujemy się w taki sposób, aby ich modele lu Ekspertów Domenowych. Eksperci ci specjalizują się
nie „przeciekały” do naszego głównego modelu. w hermetycznych domenach (sferach) wiedzy. Możliwość
Innym przykładem Generic Domain może być biblio- komunikacji merytorycznej pomiędzy ekspertami jest
teka do operowania na Grafach. Być może będziemy ją mocno ograniczona.
wykorzystywać w Module Magazynowym, aby sugerować W kontekście naszego przykładowego modelu: pojęcie
najkrótsze ścieżki, jakimi należy się poruszać pomiędzy „produktu” w Module Sprzedaży oznacza coś, co ma cha-
regałami w magazynach, aby jak najszybciej skomple- rakterystykę handlową, pewne atrybuty promocyjne, być
tować zamówienie. W tym przypadku nadbudowujemy może jest powiązane z profilami behawioralnymi klientów.
domenę Magazynu (regały, półki, korytarze) nad gene- Natomiast w Module Magazynowym jest to przedmiot,
ryczną domeną grafów (węzły, ścieżki, algorytmy wyszu- którego główną cechą jest przykładowo to, że zajmuje
kiwania ścieżek). określoną przestrzeń na regałach i być może wymaga
specjalnego traktowania podczas transportu.
Core Domain – miejsce dla DDD Jedynie ubóstwo naszego języka powoduje, że eksper-
ci domenowi socjalizujący się w sprzedaży i eksperci spe-
Omówiliśmy rodzaje domen, dla których nie stosujemy cjalizujący się w domenie magazynów używają podobnej
technik DDD. Techniki DDD mają zastosowanie (jako in- fali akustycznej, która brzmi „produkt”. Jednak na pozio-
westycja) w Core Domain. Core Domain to sfera logiki mie znaczenia to brzmienie budzi zupełnie różne skoja-
biznesowej, która jest głównym powodem, dla którego rzenia – skojarzenia z pojęciami pojawiające się w umy-
system powstaje. Być może model z Core Domain sta- śle jednego eksperta są w ogóle niedostępne dla umysłu
nowi przewagę systemu (klienta zamawiającego system) innego eksperta.
nad konkurencją. W Core Domain inwestujemy czas na- Zatem próba uogólnienia i „zsumowania” kilku mode-
szych najlepszych ludzi – Developerów oraz Ekspertów li wynikających z wiedzy osób o rozłącznych kompeten-
Domenowych. W DDD zakładamy, że modelowanie Core cjach jest zajęciem karkołomnym. W DDD takie podejście
Domain stanowi największe wyzwanie w cyklu produk- jest zaliczane do anty-wzorców i nazywa się Corporate
cji systemu. Zakładamy również, że słaby model stanowi Model Anti-Pattern.
wysokie ryzyko niepowodzenia przedsięwzięcia. Jeżeli eksperci domenowi znający na wskroś swe spe-
Podstawową techniką destylacji Core Domain jest cjalizacje nie są w stanie się porozumieć, to w jaki sposób
spisanie Dokumentu Wizji. Dokument ten powinien być możemy sobie wyobrazić sytuację, w której developerzy
krótki (np. 1/2 A4), dzięki temu skłoni jego twórcę do – nie znający zwykle żadnej z tych domen – mogą być
skupienia się na głównych (Core) założeniach, które zwy- w stanie stworzyć wspólny model kilku domen...?
kle dokładnie mapują się na czynniki przewagi, czyli Core Granice słownictwa Ekspertów Domenowych z Ubiquitous
Domain. Language wyznaczają granice modeli - Bounded Context.
/ www.programistamag.pl / 65
INŻYNIERIA OPROGRAMOWANIA
/ www.programistamag.pl / 67
INŻYNIERIA OPROGRAMOWANIA
Saga – model czasu w procesie biznesowym Każda metoda nasłuchująca konkretnego zdarzenia
w Sadze modyfikuje jej wewnętrzny stan (wzorzec pro-
Rozszerzeniem modelu zdarzeń jest Saga biznesowa. jektowy Memento) oraz sprawdza, czy warunki bizneso-
Technicznie saga jest persystentnym multi-listenerem. we potrzebne do zakończenia Sagi zostały spełnione.
Oznacza to, że obiekty Sagi nasłuchują wielu zdarzeń Więcej o Sagach na stronie projektu Levan oraz na
oraz ich stan jest utrwalany pomiędzy kolejnymi zdarze- stronie szyny NServiceBus, która wspiera model Sag po-
niami – z uwagi na potencjalnie długi czas upływający przez API silnika.
pomiędzy kolejnymi zdarzeniami.
Natomiast na poziomie koncepcyjnym Saga modeluje POZIOMY MODELU – OKIEŁZNAĆ
de facto czas. CHAOS
Listing 3 zawiera przykładowy kod Sagi, która mode-
luje przepływ zamówienia. Saga reaguje na zdarzenia: Mając do czynienia ze złożonymi modelami, możemy potrze-
stworzenia zamówienia, zatwierdzenia zamówienia, na- bować dodatkowego rusztowania „rozpinającego” strukturę
stąpienia wysyłki oraz dostarczenia zamówienia. Zakłada- modelu. Z czasem, gdy nasze rozumienie modelu pogłębia
my, że nie możemy przewidzieć kolejności pojawienia się się, zauważamy, że pewne jego elementy są ogólnym fun-
tych zdarzeń, oraz czas pomiędzy ich wystąpieniem może damentem, na którym opierają się specyficzne czynności.
sięgać miesięcy. Specyficzne czynności mają znowuż swe wariacje.
@Saga
public class OrderShipmentStatusTrackerSaga extends
SagaInstance<OrderShipmentStatusTrackerData> {
@Inject
private OrderRepository orderRepository;
@SagaAction
public void handleOrderCreated(OrderCreatedEvent event) {
data.setOrderId(event.getOrderId());
completeIfPossible();
}
@SagaAction
public void handleOrderSubmitted(OrderSubmittedEvent event) {
data.setOrderId(event.getOrderId());
// do some business
completeIfPossible();
}
@SagaAction
public void orderShipped(OrderShippedEvent event) {
data.setOrderId(event.getOrderId());
data.setShipmentId(event.getShipmentId());
completeIfPossible();
}
@SagaAction
public void shipmentDelivered(ShipmentDeliveredEvent event) {
data.setShipmentId(event.getShipmentId());
data.setShipmentReceived(true);
completeIfPossible();
}
Różne części modelu charakteryzują się różną podat- Rozwarstwienie logiki to dopiero początek
nością na zmiany. Czytelnicy zaznajomieni z Archetypa-
mi Modeli Biznesowych znają pojęcie dwóch poziomów W poprzedniej części dokonaliśmy rozwarstwienia logiki
modelu: Operational Level i Knowledge Level. Natomiast na warstwę aplikacji i warstwę logiki domenowej. War-
w DDD nadajemy modelowi jeszcze głębszą strukturę stwa aplikacji modelu (czylu Use Case/User Story/Serwi-
i wyłaniamy w nim aż cztery poziomy... sy SAO) jest odpowiedzialna za:
Rysunek 2. Poziomy modelu domenowego (wraz z mapowaniem na Archetypowe Operational i Knowlege Level)
/ www.programistamag.pl / 69
INŻYNIERIA OPROGRAMOWANIA
■■ orkiestrację domeny – scenariusz sterowania obiekta- ku – sugestie na postawie analizy behawioralnej klien-
mi domenowymi ta, jego znajomych lub wszystkich klientów (polityka!).
■■ dodatkową logikę typową dla tej konkretnej aplikacji Innym przykładem jest model dobierania rabatu (jeżeli
■■ technikalia takie jak transakcje i bezpieczeństwo klientowi przysługuje wiele rabatów, np. z uwagi na: po-
zycję zamówienia, zawartość całego zamówienia, histo-
Warstwa logiki domenowej to miejsce, w którym mode- rię zamówień klienta oraz dodatkowe rabaty: dla VIPów,
lujemy przy pomocy Building Blocks logikę ograniczoną z okazji zimy itd.). Model dobierania rabatów może być
przez Bounded Context. dostrojony (policy!) tak, aby działał na korzyść klienta lub
W tym rozdziale zajmiemy się dalszą strukturyzacją właściciela systemu...
złożonych modeli w warstwie logiki domenowej.
GRANICE AGREGATÓW –
Capability MODELUJEMY NIEZMIENNIKI
Poziom Capability zawiera klasy modelujące potencjalne Na zakończenie omawiania zaawansowanego modelowa-
możliwości, jakie oferuje nasz system. W naszym przy- nia DDD poruszymy zagadnienie określania granicy Agre-
kładowym systemie umieścimy tutaj agregaty Product gatów. Jest to najważniejszy aspekt modelowania DDD,
i Client oraz Value Object Money. Posiadając produkty, który decyduje o powodzeniu lub klęsce modelowania.
użytkowników i pieniądze, możemy potencjalnie dalej Nieodpowiednio „zakreślone” granice Agregatów powo-
modelować: handel, usługi, reklamacje itd. Na tym po- dują powstanie modeli słabo podatnych na zmiany oraz
ziomie zmiany są relatywnie niezbyt częste. nieefektywnych z wydajnościowego punktu widzenia.
W części pierwszej przyjęliśmy dosyć intuicyjne grani-
Operations ce naszych Agregatów, natomiast teraz zastanowimy się
bardziej świadomie nad ich modelem.
Poziom Operations zawiera klasy modelujące konkretne Czytelnicy doświadczeni w modelowaniu DDD mogli
operacje, jakie aktualnie wspiera nasz system. W na- zwrócić uwagę na przykładowy Agregat Order, którego
szym przykładowym systemie umieścimy tutaj agregaty granica była zakreślona dosyć „chciwie”, co mogło poten-
Order, Invoice oraz Serwis Biznesowy BookKeeper. Skła- cjalnie powodować problemy z utrzymaniem i wydajno-
danie zamówień oraz wystawianie na ich podstawie przez ścią modelu.
księgowego faktur to konkretne operacje, jakie zbudo- Agregat w definicji DDD jest spójną jednostką zmiany
waliśmy nad modelem potencjału (produktami, klientami (pracy). Od strony praktycznej Agregat powinien modelo-
i pieniędzmi). Ten poziom jest średnio podatny na zmiany. wać i enkapsulować w swym wnętrzu niezmienniki. Przy-
kładowo, jeżeli model domenowy zakłada, że zawsze:
Policy
a+b=c
Poziom polityk niejako „dostraja” poziom Operacji. Przy-
kładowo Księgowy (Serwis Domenowy BookKeeper) to wówczas Agregat powinien enkapsulować a, b, c oraz
z poziomu Operacji nalicza podatki na różne sposoby zapewniać, że po wywołaniu każdej metody Agregatu nie-
– w zależności od wdrożenia systemu w różnych krajach zmiennik jest zachowany. W naszym przykładowym Agre-
lub w zależności od tego, kto jest klientem na fakturze. gacie Order modelujemy niezmienniki: 1) dodatnie/usu-
Zwróćmy uwagę, że sposób wybrania polityki (konfigura- nięcie produktu powoduje przeliczenie ceny po rabatach,
cja wdrożenia bądź decyzja w runtime) to funkcjonalność 2) dodatnie produktu już istniejącego nie powoduje poja-
aplikacji. wienia się nowej pozycji zamówienia, a jedynie zwiększe-
Obiekty na poziomie polityk modelują wariacje operacji nie ilości na już istniejącej pozycji.
biznesowych. Model ten jest mocno podatny na zmiany.
Warto zauważyć, że polityki na poziomie technicz- Technika analizy przypadków użycia
nym to technicznie rzecz biorąc domknięcia Operacji.
Domknięcia w sensie popularnych od pewnego czasu ję- Dosyć łatwo możemy doprowadzić do nieodpowiednie-
zyków funkcyjnych. Natomiast w starszych językach im- go modelowania granicy Agregatów, jeżeli zastosujemy
plementujemy je poprzez Wzorzec Projektowy Strategii klasyczne podejście polegające na grupowaniu rzeczow-
(zob. część I). ników w „worki”. Przykładowo, jeżeli odnajdziemy w mo-
delu rzeczownik Zamówienie, to „wrzucamy” do nie-
Decission Support go kolejne, „mniejsze” rzeczowniki, tworząc zbyt duży
agregat.
Niektóre systemy posiadają rodzaj „sztucznej inteli- Techniką, która sprawdza się lepiej w DDD, jest po-
gencji” - mniej lub bardziej wyrafinowane mechaniczny wstrzymanie się od wstępnego grupowania pojęć w tego
analityczne wspierające lub wręcz podejmujące decyzje. typu „worki” do momentu analizy przypadków użycia/
W naszym przykładowym systemie mógłby być to mo- operacji domenowych i grupowania ich pod kątem spój-
del sugerowania zamienników produktów w razie ich bra- nych jednostek zmiany.
W SIECI:
PP strategiczne modelowanie agregatów: http://dddcommunity.org/library/vernon_2011
PP oficjalna strona DDD http://domaindrivendesign.org
PP wstępny artykuł poświęcony DDD http://bottega.com.pl/pdf/materialy/sdj-ddd.pdf
PP przykładowy projekt: http://code.google.com/p/ddd-cqrs-sample/
PP sagi w NServiceBus http://www.nservicebus.com/sagas.aspx
reklama
Loremipsumdolorsit
a m CHCESZ
e t , cBYĆ
o nNA se BIEŻĄCO?
ctetur
DOŁĄCZ DO NEWSLETTERA
adipiscingelit.Mauris
pos MAGAZYNU
u e r e c "PROGRAMISTA"
ondimentum
juston onvestibulum.
www.programistamag.pl
ullamgravidadolor
feugiatdiamfringilla
/ www.programistamag.pl / 71
KLUB LIDERA - IT
Michał Bartyzel, Mariusz Sieraczkiewicz
Dokumentowanie architektury
Jak zorganizować proces rozwoju architektury?
Jeśli skrupulatnie przejdziesz razem z nami przez opisane kroki, gwarantujemy, że
Ty i ludzie, z którymi współpracujesz, będziecie mieć całkowitą jasność co do tego,
jakiej konkretnie dokumentacji potrzebujecie. Zdefiniujesz kryteria, dzięki którym
określisz, czy tworzenie danego fragmentu dokumentacji będzie dla Twojego
zespołu przydatne czy nie.
T
o, co za chwilę przeczytasz, będzie mieć sens wy- problemy. Wciąż nie można połapać się w architekturze
łącznie wtedy, gdy już wielokrotnie spotkałeś się systemu. Już nie z powodu bezużyteczności dokumenta-
z problemami dotyczącymi dokumentowania i do- cji, lecz z powodu jej braku.
kumentacji w architekturze systemów informatycznych. W tej całej pogoni za nowym i lepszym jakoś zawieru-
Tak to już jest, że dopóki nie spotkasz się z określonymi szyło się ostatnie zdanie Manifestu Agile: „That is, while
trudnymi sytuacjami, pewne rzeczy mogą nie mieć dla there is value in the items on the right, we value the
Ciebie znaczenia praktycznego. Zatem jeśli: items on the left more”. Nie oznacza to, że dokumen-
tacja jest złem, którego należy się wystrzegać, lecz że
■■ pracujesz samotnie albo w bardzo małym zespole, celem projektu programistycznego jest przede wszyst-
■■ rotacja w Twoim zespole jest bardzo mała, kim działające oprogramowanie, a dokumentacja pełni
■■ nigdy nie przyszło Ci do głowy, aby w jakiś szczególny rolę pomocniczą. Stanie na stanowisku, że dobry kod
sposób dbać o dokumentowanie architektury, i częsta komunikacja całkowicie zastąpią dokumenta-
cję, jest równie nonsensowne, co opinia, że za pomo-
to spokojnie możesz przejść do kolejnego ciekawego cą UMLowych diagramów można napisać cały system,
artykułu w magazynie lub potraktuj ten tekst poglądo- a potem wystarczy już „tylko” wygenerować kod. Rze-
wo i jako przedstawienie problemów pojawiających się czywistość tak nie działa. Nie ma jednego sposobu na
w projektach. Lecz jeśli: załatwienie wszystkich problemów. Wspomniane zdanie
z Manifestu mówi, że:
■■ w Twoim zespole często należy wprowadzać nowe oso-
by do projektu, ■■ poszerz swoje myślenie o dokumentacji; dokumenta-
■■ próbowałeś już wielokrotnie dokumentować archite cja to nie tylko tekst, obrazki i diagramy, lecz również
kturę i nie zdawało to egzaminu, kod źródłowy, testy;
■■ masz wrażenie, że automatyczne generowanie tysięcy ■■ celem projektu jest stworzenie oprogramowania, a do-
stron dokumentacji jest absolutnie bezcelowe, kumentacja jest o tyle przydatna, o ile wspomaga ten
■■ w Twoim zespole wiedza o systemie mieści się w więk- cel;
szości w głowach programistów, ■■ są różne konteksty użycia dokumentacji, w zależności
od kontekstu dokumentacja może się różnić; raz może
to znajdziesz tu kilka przepisów, jak rozwiązać wspo- to być prosty odręczny rysunek, raz złożony diagram
mniane problemy. W tym artykule chcemy przeprowadzić klas, innym razem kod źródłowy jest wystarczającą
Ciebie i Twój zespół przez fragment procesu zmierzają- dokumentacją.
cego do odtworzenia dokumentacji złożonego systemu
informatycznego. W związku z powyższym, zanim rozpoczniesz działa-
nia dokumentowania systemu, absolutną koniecznością
SENS DOKUMENTOWANIA jest precyzyjne określenie po co dokumentacja w ogóle
powstaje.
We have come to value working software over compre-
hensive documentation – stwierdza Manifest Agile. Zaraz CEL TWOJEJ DOKUMENTACJI
potem rzesze zespołów uwolnionych spod jarzma z ze-
garmistrzowską dokładnością zdefiniowanych procesów, Tak, właśnie Twojej. Podejście do dokumentowania archi-
każących im dokumentować nawet mrugnięcie okiem, ru- tektury systemu w każdym zespole, w każdym projekcie
szyły w stronę Agile z nowymi hasłami na ustach „Precz będzie inne. To jest właśnie główny kłopot w tym obsza-
z dokumentacją!”, „Dobry kod i testy to również doku- rze. Mamy UML, mamy narzędzia, lecz jak je wykorzy-
mentacja!”. Pojawiły się nowe praktyki, lecz zostały stare stać, aby stworzyć coś rzeczywiście przydatnego?
Zwróć uwagę, że jeśli postawimy kwestię właśnie w ten Krok 2. Zbieranie informacji o problemach
sposób: „jak używać dostępnych narzędzi, aby stworzyć
użyteczną dokumentację”, niemal automatycznie nasu- W drugim kroku zbierzesz informacje o tym, jakiego ro-
wa się pytanie: „co i komu jest właściwie potrzebne?”. dzaju problemy wystąpiły w tracie prac, którym mogłaby
I o to chodzi! Jeśli będziemy po prostu dokumentować, zaradzić dokumentacja. Możesz ten krok wykonać samo-
to powstanie duża ilość dokumentacji bez żadnego spre- dzielnie, lecz więcej różnorodnych informacji uzyskasz,
cyzowanego zastosowania. Jeśli najpierw określmy kon- gdy zaangażujesz w niego cały swój zespół. Zbieranie
tekst użycia dokumentacji, kto, kiedy, do czego będzie jej problemów odbywa się poprzez wypełnienie treścią na-
używał, to będzie powstawało tylko tyle dokumentacji, ile stępującego zdania:
trzeba i ani bajta więcej.
■■ W trakcie…
ETAP 1. POSZUKIWANIE PROBLEMÓW ■■ (kto?)…
■■ miał problem z…
W przypadkach, z którymi mamy do czynienia najczęściej, ■■ ponieważ…
czyli podczas odtwarzania dokumentacji już istniejącego ■■ i spowodowało to…
systemu, cele dokumentacji najłatwiej zdefiniować, wy-
chodząc od problemów, które występują, gdy dokumen- Każda z części powyższego zdania ma swoje specyficzne
tacji nie ma. zadanie, dlatego każdą z nich omówimy osobno.
„W trakcie…” uwypukla miejsce, w którym problem się
Krok 1. Proces wytwarzania oprogramowania pojawia. Wpisz tu nazwę etapu z narysowanego wcześniej
procesu wytwarzania oprogramowania, w którym bierzesz
Żeby namierzyć problem z dokumentacją, trzeba wskazać udział. Od miejsca w procesie może zależeć forma doku-
miejsce, w którym występuje. Z tego powodu pierwszym mentacji oraz sposób jej udostępniania. Na przykład jeśli
krokiem będzie narysowanie procesu wytwarzania opro- kłopot występuje podczas etapu wdrożenia, to ten fragment
gramowania, w którym zespół bierze udział. Niestety nie dokumentacji najpewniej przybierze bardzo formalną po-
wystarczy, że będziesz „miał go w głowie”. Jakoś tak to stać, jeśli jednak dotyczy etapu implementacji, to być może
magicznie działa, że gdy narysujesz proces, to zaczynasz wystarczy odręczny schemat na dużej kartce przyklejonej
patrzeć na niego z innej perspektywy – jako obserwator, na ścianie w pomieszczeniu, w którym pracuje zespół.
nie jako uczestnik. Twoje postrzeganie jest wtedy szersze Część „(kto?)…” identyfikuje osobę, która miała opi-
i nieco bardziej obiektywne. Nawet jeśli używasz znanej sywany problem. W tym miejscu mogą pojawić się: imię
metodyki, również przygotuj rysunek. Proces, w którym faktycznej osoby, rola pełniona w projekcie, stanowisko.
bierzesz udział, z pewnością wygląda nieco inaczej, niż Ta część definiuje odbiorcę dokumentacji. Jest o tyle istot-
wzorcowy diagram w materiałach szkoleniowych. To wła- na, że innych informacji potrzebuje programista, innych
śnie „nieco” może sprawić, że kłopoty występują albo zni- projektant, a jeszcze innych architekt. Dzięki precyzyj-
kają. Efektem tego kroku będzie schemat blokowy, które- nemu nazwaniu odbiorcy, będziemy potrafili uwypuklić te
go przykład znajdziesz na Rysunku 1. elementy systemu, które są istotne dla tej właśnie osoby.
/ www.programistamag.pl / 73
KLUB LIDERA - IT
Autor
„Miał problem z…” określa faktyczny moment poja- menty, które możesz przedstawić jako poparcie swoich
wienia się problemu. Zatrzymaj się na chwilę, bo trze- działań odtwarzania dokumentacji.
ba powiedzieć o bardzo ważnej rzeczy. W tym miejscu Jeszcze mała podpowiedź na koniec. Zbieranie powyż-
mogą pojawić się wyłącznie czynności, z którymi miała szych informacji przebiega sprawniej, jeśli dasz ludziom
problem dana osoba. Czynność to coś, co możesz wy- tabelę do wypełnienia. W nagłówkach kolumn wpisz po-
konać, to określone działanie, które ma swój początek szczególne części zdania i ewentualne objaśnienia. W po-
i koniec, czynność odbywa się w czasie. Dlaczego jest to szczególnych wierszach ankietowani będą opisywać ko-
takie ważne? Jeśli określisz problemy następująco: lejne problemy. Forma tabeli sprawia, że nie trzeba za
każdym razem przepisywać powtarzalnych fragmentów,
■■ miał problem z bazą danych, a osoba ją wypełniająca może skupić się na myśleniu za-
■■ miał problem z testami, miast na pisaniu.
■■ miał problem z komunikacją z Product Ownerem,
Etap 2. Definiowanie kryteriów
to niewiele z tego wynika. Jedyne bowiem, co możesz
zaradzić w takich wypadkach, to: dokumentacja bazy Większość pracy masz już za sobą. Teraz pozostało je-
danych, dokumentacja testów, porozmawiać z Product dynie ostateczne sformułowanie kryteriów, które musi
Ownerem. Takie definiowanie problemów jest zbyt ogólne spełniać dokumentacja architektury Twoje go systemu.
i utrudnia znalezienie środka zaradczego. Jeśli natomiast Pojedyncze kryterium jest określone poprzez trzy nastę-
zgodnie z przedstawioną zasadą skoncentrujesz się na pujące pytania:
czynnościach np.:
■■ Kto będzie używał dokumentacji architektury,
■■ miał problem z określeniem, które procedury składo- które osoby, które role, które pełnione funkcje?
wane uruchamiają się w żądaniu X, ■■ Kiedy będzie używał dokumentacji architektury,
■■ miał problem z napisaniem testu jednostkowego do Y, na którym etapie procesu wytwarzania oprogramowania,
■■ miał problem z tym, że długo czekał na odpowiedź Pro- ■■ Czego musi się dowiedzieć z dokumentacji archi-
duct Ownera, tektury, na jakie konkretne pytania musi odpowiadać
dokumentacja.
to niemal od razu przychodzi do głowy, które elementy
systemu wymagają objaśnienia i które aspekty współpra- Podobnie jak poprzednio, wygodnie będzie zebrać wszyst-
cy z Product Ownerem należy poprawić. Definiując pro- kie kryteria w tabeli, której nagłówki kolumn będą za-
blemy, odnoś się do konkretnych czynności. wierały powyższe pytania. Aby wypełnić tabelę kryteriów,
Część „ponieważ…” dookreśla potencjalne braki w do- skorzystaj wyodrębnionych wcześniej problemów w spo-
kumentacji. Tutaj jest miejsce na wpisanie, czego dana sób przedstawiony na Rysunku 2.
osoba nie wiedziała lub jaki błąd popełniła. Zgodnie z obietnicą złożoną na początku, po sumien-
Na koniec została jeszcze część „i spowodowało nym wykonaniu wszystkich opisanych kroków, w tym
to…”. Na tym etapie odpowiadasz na pytania: Jaki skutek momencie dysponujesz listą kryteriów, które musi speł-
miał ten błąd dla: terminów, innych zależnych procesów, niać dokumentacja architektury Twojego systemu. Od tej
stabilności systemu, podwykonawców, klientów? Można pory za każdym razem, gdy zaplanujesz stworzenie ja-
powiedzieć, że ta część zdania wyodrębnia problemy biz- kiejś części dokumentacji, zweryfikuj to pod kątem swo-
nesowe związane z dokumentacją. Tutaj określisz argu- ich kryteriów.
Trenerzy i konsultanci w firmie BNS IT. Badają i rozwijają metody psychologii pro-
gramowania, pomagające programistom lepiej wykonywać ich pracę. Na co dzień
Autorzy zajmują się zwiększaniem efektywności programistów poprzez szkolenia,
warsztaty oraz coaching i trening.
Blog o infrastrukturze IT
WDROŻENIA
Wojciech Holisz
Highsky.com
– projekt, oprogramowanie i wdrożenie pla
tformy inwestycyjnej highsky.com zintegro
wanej z platformą MetaTrader 5.
Projekt został zrealizowany przez zespół Positive Power Sp. z o.o. na zlecenie
czeskiego domu maklerskiego HighSky Brokers A.S. W artykule zaprezentowano
wybrane etapy realizacji projektu highsky.com, a także sposoby integracji plat-
formy MetaTrader 5 z zewnętrznymi aplikacjami. Opisano również język skryp-
towy MQL5, który został wbudowany w platformę i umożliwia rozszerzenie jej
funkcjonalności.
P
latforma MetaTrader 5 to zestaw nowoczesnych in- Język MQL5 jest językiem wysokiego poziomu, zorien-
strumentów umożliwiających handel na rynku wa- towanym obiektowo. Składania przypomina język C++.
lutowym oraz giełdowym. Oprócz możliwości za- Podobnie jak C++, język MQL5 cechuje się statyczną
wierania transakcji platforma oferuje również szereg typizacją. Do naszej dyspozycji zostały oddane typy da-
różnorodnych narzędzi analitycznych, pozwalających na nych pozwalające na wykonywanie operacji na liczbach
ocenę bieżących oraz historycznych danych. W ich skład całkowitych (int, uint, long), zmiennopozycyjnych (flo-
wchodzą m.in. dynamiczne wykresy cenowe. at, double) oraz wartościach logicznych (bool), a także
Platforma składa się z wielu elementów. Wśród nich możliwość definiowania własnych typów danych w postaci
wyróżniamy m.in. aplikacje klienckie (terminale), służą- struktur oraz klas.
ce do zlecania operacji handlowych i analizy danych oraz Skrypty tworzone w MQL5 dzielą się na kilka typów.
wielofunkcyjne serwery, m.in.: Pierwszy rodzaj to tzw. Expert Advisor, czyli roboty, dzię-
ki którym można całkowicie zautomatyzować procesy
■■ przetwarzające operacje handlowe, handlowe. Roboty potrafią na bieżąco śledzić sytuację na
■■ udostępniające dane historyczne, giełdzie i na podstawie dokonanej analizy zlecać opera-
■■ przechowujące kopie zapasowe, cje kupna bądź sprzedaży. Drugi rodzaj to proste skrypty,
których zadaniem jest zwykle wykonanie jednego specy-
dostępowe (access points), z którymi łączą się terminale. ficznego zadania, a następnie zakończenie pracy. Z kolei
Użytkownik końcowy uzyskuje dostęp jedynie do ter- biblioteki (Library) nie są typowymi skryptami, ale zbio-
minala, a pozostała infrastruktura jest ukryta za serwe- rem funkcji, które można wielokrotnie wykorzystywać w
rami dostępowymi. Dzięki takiej architekturze systemu innych skryptach.
znacznie podnosi się bezpieczeństwo danych przechowy- W listingu 1 przedstawiam kod prostego programu na-
wanych na serwerach. pisanego w C++, który wyświetla na ekranie tekst oraz
Posiadacze smartphone’ów i tabletów mogą skorzy- jego odpowiednik w języku MQL5.
stać z aplikacji mobilnej, która umożliwia przeglądanie Jak widać, języki te są do siebie bardzo zbliżone. Skrypt
wykresów oraz handel. Aplikacja jest dostępna na tele- MQL5 zawiera funkcję o nazwie “OnStart”, która jest od-
fony iPhone oraz z systemem Android, można je pobrać powiednikiem funkcji “main” w języku C++ oraz definicję
nieodpłatnie ze strony http://www.metatrader5.com/en/ klasy. Skrypt można przetestować w aplikacji MetaEditor.
download. Aby utworzyć nowy skrypt, należy wybrać opcję “New” z
menu “File”, w oknie zaznaczamy “Script” i przechodzimy
MQL5 dalej. W kolejnym oknie możemy określić nazwę skryptu,
jego autora oraz parametry wejściowe. W pierwszym polu
Aplikacja kliencka MetaTradera została wyposażona w wpisujemy nazwę i zatwierdzamy przyciskiem “Finish”.
język skryptowy MQL5. Przy pomocy języka MQL5 moż- Otworzy się nowe okno z domyślnym szablonem, który
na tworzyć zarówno proste skrypty agregujące dane czy usuwamy i wklejamy kod naszego skryptu.
powiadamiające o zmianie kursów, jak i zaawansowane Skrypt kompilujemy przy pomocy przycisku “Compi-
automaty, które potrafią samodzielnie przeprowadzać le” lub klawisza F7, a uruchamiamy, wybierając pozycję
operacje handlowe. “Start” z menu “Debug” lub klawiszem F5. Jeżeli nie uru-
Listing 1. Przykładowy program w C++ oraz jego odpowie chomiliśmy jeszcze MetaTradera, edytor zrobi to za nas,
dnik w MQL5 a następnie otworzy jeden wykres. W oknie “Toolbox”
umieszczonym przy dolnej krawędzi ekranu w zakład-
#include <iostream>
#include <string> ce “Eksperci”, znajdziemy tekst wyświetlony przez nasz
skrypt.
class Person
{
Okno z wykresem otwiera się, ponieważ skrypty nie
public: mogą zostać uruchomione jako samodzielne instancje.
Person(const std::string& name) : name(name) Każdy skrypt musi zostać “załączony” do wykresu, aby
{
} możliwe było jego wykonanie. Chcąc uruchomić skrypt z
poziomu MetaTradera, wyszukujemy go w oknie “Naviga-
const std::string& getName() const
{ tor” i wybieramy z menu podręcznego pozycję “Attach to
return name; Chart”.
}
protected: Oczywiście język zawiera również instrukcje sterujące.
std::string name; Instrukcje warunkowe, pętle oraz instrukcje wyboru defi-
};
niujemy identycznie jak w języku C++.
int main(int argc, char* argv[])
{ Listing 2. Przykład ze skryptu z pętlą oraz parametrami
Person* person = new Person("John");
wejściowymi
std::cout
<< "Hello World! My name is " #property copyright "Copyright 2012, MetaQuotes
<< person->getName() Software Corp."
<< "." << std::endl; #property link "http://www.mql5.com"
#property version "1.00"
delete person;
#property script_show_inputs
return 0;
} input uint from;
input uint to;
// MQL5
class Person void OnStart()
{ {
public: for (uint i = from; i <= to; ++i)
Person(string name) : name(name) {
{
if (i % 15 == 0)
}
{
string getName() const Print("FizzBuzz");
{ }
return name; else if (i % 3 == 0)
} {
protected: Print("Fizz");
string name; }
}; else if (i % 5 == 0)
{
void OnStart()
{ Print("Buzz");
Person* person = new Person("John"); }
else
Print("Hello World! My name is " + person. {
getName() + "."); Print(i);
}
delete person; }
} }
/ www.programistamag.pl / 77
WDROŻENIA
Podobnie jak do programów pisanych w innych językach, zapoznaniu się z tajnikami języka MQL5 można spró-
do skryptów MQL5 również możemy przekazywać parame- bować swoich sił w organizowanych co roku zawodach
try wejściowe. Parametry wejściowe definiowane są tak Automated Trading Championship (http://championship.
samo, jak zmienne globalne, ale poprzedzamy je słowem mql5.com).
kluczowym “input”. W przykładzie zdefiniowane zostały dwa
parametry wejściowe o nazwach “from” oraz “to”. WORTAL HIGHSKY BROKERS
W powyższych przykładach zostały umieszczone także
“właściwości skryptu”. Każdy skrypt może zostać opatrzony Wortal inwestycyjny https://www.highsky.com/ został
szeregiem właściwości, w których możemy umieścić m.in. wykonany na potrzeby działającego na terenie Czech
informacje o autorze skryptu lub jego wersji. Właściwości domu maklerskiego HighSky Brokers, A.S. Serwis, skie-
definiujemy przy pomocy polecenia “#property”, po któ- rowany do użytkowników czesko- i anglojęzycznych, ofe-
rym występuje nazwa oraz wartość właściwości. Umiesz- ruje dostęp do aplikacji MetaTrader oraz wielu materiałów
czenie w skrypcie właściwości “script_show_inputs” powo- szkoleniowych na temat działania platformy i funkcjono-
duje, że podczas jego uruchomienia wyświetli się okno z wania giełdy.
możliwością wprowadzenia parametrów wejściowych. Dzięki integracji z systemem transakcyjnym MetaTra-
Przedstawione przykłady zawierały jedynie podsta- der, w serwisie istnieje możliwość założenia rachunku
wowe elementy samego języka MQL5. Utworzymy teraz inwestycyjnego “demo” bądź rachunku “rzeczywistego”.
skrypt Expert Advisor, który umożliwi integrację z apli- Rachunki demo służą wyłącznie celom edukacyjnym.
kacją MetaTrader 5. Z menu “File” wybieramy pozycję Dzięki nim użytkownicy mogą zapoznać się z zasadami
“New”. W kreatorze zaznaczamy “Expert Advisor (templa- funkcjonowania giełdy oraz testować różne strategie gry
te)” i podajemy nazwę skryptu. W kolejnym etapie wybie- bez obawy o utratę środków, gdyż wszelkie operacje han-
ramy zdarzenia, na które reagować będzie nasz skrypt. dlowe przeprowadzane na takich rachunkach są traktowa-
Po zatwierdzeniu przyciskiem “Finish” otworzy się nowe ne jako fikcyjne i nie skutkują faktycznym zakupem lub
okno z szablonem skryptu. Usuwamy jego zawartość i sprzedażą. Rachunki demo wiążą się z jeszcze jedną funk-
wklejamy kod z listingu 3. cjonalnością serwisu - za ich pośrednictwem użytkownicy
Metody “OnInit” oraz “OnDeinit” są wywoływane od- wortalu mogą wziąć udział w organizowanym przez dom
powiednio podczas uruchamiania oraz zamykania skryp- maklerski HighSky Brokers konkursie. Każdy jego uczest-
tu. W powyższym przykładzie zostały wykorzystane do nik rejestruje testowy rachunek demo, a następnie stara
uruchomienia oraz zatrzymania zegara. Zegary generują się zgromadzić na swoim koncie jak największy kapitał.
zdarzenia co określoną liczbę sekund. Nasz zegar urucha- Swoją aktualną pozycję można sprawdzić w codziennie
mia się co 10 sekund i wyświetla ile razy został wywołany aktualizowanej liście rankingowej. Konkurs trwa kilka ty-
od momentu uruchomienia skryptu. godni, a na zwycięzców czekają nagrody.
Metoda “OnTick” jest wywoływana w momencie, gdy W przeciwieństwie do rachunków demo, rachunki rze-
nastąpi aktualizacja cen na wykresie, do którego załączo- czywiste umożliwiają faktyczną grę na giełdzie i są aktyw-
ny jest nasz skrypt. Metoda z przykładu pobiera oraz wy- ne dopiero po podpisaniu stosownych umów o współpracy
świetla w konsoli najnowsze dostępne wartości cen kupna z domem maklerskim. Po założeniu rachunku użytkow-
oraz sprzedaży. nik otrzymuje jednoczesny dostęp do aplikacji klienc-
Skrypty Expert Advisor pozwalają również na integrację kiej MetaTrader 5 oraz specjalnie przygotowanej strefy
z elementami interfejsu aplikacji. Ostatnia z przykładowych klienta, gdzie może zweryfikować swoje dane oraz drogą
metod “OnChartEvent” wyświetla współrzędne punktu, w elektroniczną uzupełnić stan konta. Płatności on-line są
którym nastąpiło kliknięcie oraz kod naciśnięcia klawisza. realizowane przy pomocy systemu Global Payments, za-
Możliwości naszych skryptów możemy rozszerzać po- pewniającego wysokie bezpieczeństwo transakcji. Jedną
przez import funkcji z zewnętrznych bibliotek DLL. Poniż- z cech - wyróżniającą płatności GP na tle innych - jest
szy skrypt wywołuje popularną funkcję “MessageBox” z konieczność cyfrowego podpisywania wszystkich danych
biblioteki systemowej. przesyłanych pomiędzy serwisem a systemem płatności,
dzięki czemu płatności są odporne na niepożądane mody-
Listing 4. Wywołanie funkcji z zewnętrznej biblioteki DLL fikacje danych.
Wortal pełni również funkcję informacyjną oraz edu-
#import "User32.dll" kacyjną. Zawarte w nim artykuły szkoleniowe pozwalają
int MessageBoxW(long, string, string, int);
#import nowym użytkownikom zapoznać się z zasadami gry na
giełdzie, uczą także obsługi aplikacji MetaTrader. Z kolei
void OnStart() bieżące wiadomości ze świata giełdy zapewniają dopływ
{
cennych informacji pomocnych podczas zawierania trans-
MessageBoxW(0, "Hello World!", "Message", 0);
} akcji handlowych.
Serwis internetowy udostępnia również aktualne no-
towania w postaci przewijanego paska widocznego na
Dokumentacja wraz z przykładami użycia języka MQL5 każdej podstronie oraz stale uaktualnianych wykresów
jest dostępna pod adresem http://www.mql5.com/. Po (liniowych, słupkowych oraz świecowych). Dodatkowo na
int OnInit()
{
Print("Bot started.");
EventSetTimer(10);
return 0;
}
EventKillTimer();
}
void OnTick()
{
const string symbol = Symbol();
MqlTick tick;
SymbolInfoTick(symbol, tick);
Print("Symbol: ", symbol, ", Bid: ", tick.bid, ", Ask: ", tick.ask);
}
void OnTimer()
{
static uint i = 1;
case CHARTEVENT_KEYDOWN:
Print("You pressed key with code ", lparam, ".");
break;
}
}
wykresach liniowych prezentowane są dane makroeko- serwera, jeżeli wystąpi konieczność np. zwiększenia mocy
nomiczne obrazujące stan gospodarki. Do generowania obliczeniowej lub udostępnienia większej powierzchni
wszystkich wykresów w wortalu wykorzystano zewnętrz- dyskowej. Serwis WWW jest hostowany na serwerze pra-
ne narzędzie Google Charts Tools, dzięki czemu zoptyma- cującym pod kontrolą systemu Debian, a aplikacja inte-
lizowano zapotrzebowanie na moc obliczeniową serwera, grująca serwis WWW z platformą MetaTrader 5 w syste-
na których hostowany jest serwis. mie Windows Server.
Publikowane artykuły to nie jedyne źródło informacji na Strona WWW została wykonana w języku PHP 5, a dane
temat giełdy. W serwisie znajdziemy również wykaz semi- przechowywane są w relacyjnej bazie MySQL 5. W celu
nariów wraz z możliwością rejestracji. Na seminariach - za- podniesienia bezpieczeństwa zainstalowano rozszerzenie
równo w formie tradycyjnych spotkań, jak i elektronicznych Suhosin dla języka PHP oraz rozszerzenie mod_security
konferencji - poruszane są tematy związane z giełdą oraz do serwera Apache. Rozszerzenie Suhosin to zaawanso-
obsługą inwestycji za pośrednictwem platformy MetaTrader. wany system, którego zadaniem jest ochrona serwerów
oraz użytkowników przed potencjalnymi zagrożeniami
TECHNOLOGIE występującymi w języku PHP i aplikacjach tworzonych
przy jego użyciu.
Aplikacje umieszczono na dwóch serwerach wirtualnych. Bezpieczeństwo danych podnosi również zastosowanie
Wirtualizacja daje możliwość łatwej zmiany parametrów certyfikatów SSL. Dostęp do wszystkich podstron witry-
/ www.programistamag.pl / 79
WDROŻENIA
ny highsky.com jest możliwy wyłącznie poprzez protokół Jedną z zastosowanych optymalizacji jest instalacja
HTTPS. Częstym niedopatrzeniem popełnianym podczas rozszerzenia APC. Moduł APC pełni dwie funkcje. Pierwsza
tworzenia serwisów WWW jest wymuszanie szyfrowania to kompilacja kodu do postaci kodu operacji (opcode),
SSL jedynie przy formularzach logowania czy rejestracji, dzięki czemu kod nie musi być przetwarzany za każdym
a wyłączanie go na pozostałych podstronach. Zabezpie- razem, gdy skrypt jest wywoływany. Druga funkcja APC
cza to wprawdzie przed przechwyceniem hasła wpisywa- to możliwość korzystania z pamięci operacyjnej do prze-
nego podczas logowania, ale pozwala na przechwycenie chowywania danych (np. wyników zapytań wykonywa-
ciasteczka identyfikującego zalogowanego użytkownika, nych na bazie danych). Dzięki cache’owaniu wyników wy-
jeżeli użytkownik przejdzie na nieszyfrowaną podstro- konywanych zapytań, serwer może szybciej generować
nę. Pomimo pewnych słabości protokołu HTTPS należy dynamiczne strony WWW. Zastosowanie APC zwiększa
go włączać w całym serwisie. Takie rozwiązanie wymu- szybkość wykonywania skryptów oraz zmniejsza zapo-
sza szyfrowanie wszystkich treści, łącznie z obrazami, co trzebowanie na pamięć podczas ich wykonywania.
wiąże się z nieco większym zużyciem mocy obliczeniowej Integrację serwisu z platformą MetaTrader 5 wykonano
procesora, ale bezpieczeństwo danych powinno stać na przy pomocy API udostępnionego przez autorów oprogra-
pierwszym miejscu. mowania. W przeciwieństwie do języka MQL5, API nie jest
Dostęp do plików z kodem źródłowym oraz plików za- dostępne publicznie i korzystanie z niego jest możliwe
wierających ważne dane (np. hasła dostępu do bazy) zo- dopiero po wykupieniu odpowiedniej licencji. API umoż-
stał zabezpieczony poprzez umieszczenie ich w katalogu, liwia m.in. zakładanie rachunków demo oraz rachunków
do którego serwer WWW nie ma dostępu. Serwer WWW rzeczywistych, zarządzanie użytkownikami, dostęp do
posiada dostęp jedynie do zasobów statycznych np. gra- bieżących i archiwalnych notowań oraz zlecanie operacji
fik, arkuszy CSS oraz skryptów JavaScript. Istotne frag- handlowych.
menty kodu źródłowego zostały dodatkowo zaszyfrowane. Aplikacja integrująca serwis z platformą MetaTrader
Serwis WWW działa wydajnie dzięki zastosowaniu wer- 5 została wykonana przy użyciu technologii .NET w ję-
sji 5.3 języka PHP, która zyskała wiele poprawek związa- zyku C#. Technologia .NET została wybrana ze względu
nych z szybkością działania. Większą wydajność serwisu na możliwości łączenia kodu natywnego (API platformy)
osiągnięto poprzez zastosowanie wielopoziomowej pa- z kodem zarządzanym oraz możliwościami oferowanymi
mięci podręcznej (cache), czyli mechanizmu polegające- przez Windows Communication Foundation. Aplikacja wy-
go na zapamiętywaniu często wykorzystywanych danych, korzystuje API platformy do zakładania rachunków oraz
ale zwykle dostępnych w źródłach o wolniejszym czasie udostępniania najnowszych notowań. Dzięki zastosowa-
dostępu lub takich, których generowanie zajmuje więcej niu WCF aplikacja może zostać uruchomiona samodziel-
czasu. nie lub jako usługa działająca pod kontrolą serwera IIS.
Positive Power
Positive Power Sp. z o. o. skupia kilkudziesięciu ekspertów z branży IT, webdesignu
oraz działań kreatywnych. Agencja specjalizuje się w tworzeniu stron internetowych,
dedykowanych platform e-commerce oraz zaawansowanych aplikacji oraz szeroko
pojętym marketingu internetowym. W ciągu 10 lat istnienia przekonała do siebie
kilkaset firm i instytucji, m.in.: Onet.pl, Libet SA, Mennica Polska SA, Mitsubishi Elec-
tric, Subway, Intersport SA, Greenpeace, 8a.pl.
/ www.programistamag.pl / 81