piątek, 30 marca 2012

Nowa ksiazka o testowaniu jednostkowym


Dnia 28 marca 2012 pojawiła sie oficjalnie książka: Practical Unit Testing with TestNG and Mockito napisana przez Tomka Kaczanowskiego. Wspominałem w jednym z poprzednich wpisów (na temat testowania wyjatków), że ta książka nadchodzi. Już oficjalnie jest dostępna do kupienia na practicalunittesting.com do czego zachecam.

Dlaczego o tym pisze?

Powodów jest kilka.

Po pierwsze jest to książka polskiego autora (co prawda po angielsku, ale spolszczenie jest w przygotowaniu), a tych należy wspierać. Łatwiej (podobno) jest napisać książkę techniczną po angielsku i później ją przetłumaczyć na nasze, niż pisać po polsku i później się męczyć.

Po drugie jest to kompletny, uporządkowany i aktualny zbiór technik z przykładami, jak pisać dobre testy. Książka wyjaśnia wszelkie wątpliwości związane z nazewnictwem, konwencjami i dobrymi praktykami, jak i daje wiele cennych wskazówek.

Dla kogo jest ta książka?

Książka jest bardzo dobra dla osób, które chcą właśnie zacząć pisać testy jednostkowe do swojego kodu i nie wiedzą jak wystartować. Książka wprowadza krok po korku do lepszego świata kodu pokrytego testami. Również dla startych wyjadaczy znajdzie się sporo cennych informacji. Dalsze rozdziały książki omawiają zaawansowane zagadnienia ze świata testowania. Tomek wskazuje również dobre praktyki jak pisać testy, a także prezentuje narzędzia powiązane z nimi(jak catch-exception czy testy mutacyjne).

Dlaczego więc się wypowiadam o tej książce, skoro dopiero co się ukazała?

Dobra, dość tej krypto reklamy, czas na konkrety ;)

Powodem dla którego piszę na blogu o tej książce, jest fakt, że brałem udział w jej powstawaniu. Mianowicie robiłem korektę (review) książki, za co dostałem podziękowania uwiecznione na jednej ze stron w książce:
In various different ways a number of people have helped me with writing this book – some by giving feedback, others by comforting me in times of doubt.
Marcin Stachniuk was the first person to offer to review the book in the early stages of its being written, and at the same time the most persistent of my reviewers. He read every part of the book and gallantly overcame the obstacles I frequently put in his way: frequent releases, constant juggling of the contents, minor piecemeal adjustments, etc.
Nadszedł teraz czas podzielenia się moimi wrażeniami ze wspólnej pracy z autorem.

Wszystko zaczęło się na konferencji GeeCON 2011 w Krakowie, Podczas (za)ostatniego dnia konferencji (tzw. Community Day) Tomek miał swoją prezentację pt.: Who watches the watchmen? - on quality of tests. Na prezentacji trochę się wynudziłem, gdyż otarłem się już o większość omawianych tam zagadnień. Pod koniec jednak prelegent pochwalił się, że właśnie pisze książkę o testach. Wówczas jeden z uczestników (nazwisko do wiadomości redakcji) zaproponował, że chętnie by zrobił review jego książki. Bardzo zaciekawiła mnie ta inicjatywa, i mimo iż wszyscy już wychodzili z sali, zostałem chwilę dłużej, aby przysłuchać się rozmowie na temat recenzowania książki.

Tomek zaproponował aby chętni się do niego zgłosili mail’owo, aby ustalić szczegóły współpracy.  I tak to się zaczęło. Stwierdziliśmy, że najlepszą formą wymiany informacji będzie założona na Google prywatna grupa dyskusyjna, poprzez którą Tomek będzie podsyłać nowe wersje książki. Tym kanałem można również podyskutować i wyjaśnić ewentualne kwestie sporne. Rozwiązanie to sprawdziło się.

Preferowanym sposobem zgłaszania uwag, było odsyłanie aktualnego pdf’a ze wstawionymi komentarzami w teksie, w formie żółtych karteczek. Funkcjonalność taką daje nam Foxit Reader. Dzięki temu Tomek widział dokładnie, którego miejsca dotyczy uwaga. Było to również wygodne dla mnie, gdyż fragmenty czytałem na ekranie komputera. Niektórzy zgłaszali swoje uwagi w postaci wiadomości na grupie, ale dla mnie żółte karteczki i tak były lepsze, gdyż dokładniej wskazywały miejsce, gdzie jest coś nie tak.

Na podstawie e-maili naliczyłem jakieś 20 „release’ów” książki, tzn. fragmentów, które Tomek podsyłał do korekty. Początkowo przychodziły pojedyncze rozdziały do czytania, ale lepszym wyjściem, jak się później okazało, było podsyłanie całości. Dzięki temu nie było rozdziałów wyrwanych z kontekstu i było widać ogólnego zarys całości książki, w którą stronę ona dąży.

Koleje wersje pojawiały się z różną częstotliwością. Czasem było kilka w tygodniu, a czasem była cisza przez dłuższy czas. Ale to już była kwestia Tomka jak sobie organizuje pracę nad książką. Ze swojej strony mogę powiedzieć, że często nowe wersje się pojawiały, jak akurat mocno byłem zajęty innymi sprawami i niemiałem możliwości aby usiąść od razu do lektury. Kilka razy dogoniła mnie kolejna wersja i wtedy już trzeba było się zmobilizować i przeczytać to co tam Tomek naskrobał :)

Co do zgłaszanych uwag, to Tomek zazwyczaj je akceptował. Jak nie widziałem poprawy w następnej wersji, to początkowo się czepiałem, że moje sugestie są nieuwzględniane, ale w późniejszym czasie doszedłem do wniosku, że nie powinienem się tym przejmować. W końcu to książka Tomka i Jego koncepcja, a nie moja. Jedynie sprawy edytorsko/estetyczne (zła czcionka, rozjeżdżające się tabelki) były dla mnie bardzo irytujące, ale to chyba przez spędzenie dawniej sporej ilości czasu z LaTeX’em. Tomek postanowił zostawić to sobie na sam koniec, przez co ciągle widziałem te niedoskonałości. To była chyba jedyna pierdółka, która mi troszkę przeszkadzała w naszej współpracy.

Moje zaangażowanie zostało wynagrodzone cytowanymi wyżej podziękowaniami w książce (Dzięki Tomek!). Właściwie to nie nawet nie ustaliliśmy szczegółów współpracy, tzn. co ewentualnie będę z tego miał. Do samego końca nie wiedziałem nawet, czy pojawią się te podziękowania w książce (po cichu na nie liczyłem). W końcu w jednej z ostatnich wersji książki zobaczyłem, że są :) Świadczy to o tym, że moje zaangażowanie było kompletnie non-profit.

Podsumowując współpracę cieszę się, że wziąłem udział w tym przedsięwzięciu. Było to bardzo ciekawe doświadczenie i mogłem przez to poznać proces powstawania książki. Robienie review sprawia wielka frajdę, czego zwieńczeniem jest oczywiście ukazanie się książki „na produkcji”. Szczerze polecam każdemu zaangażowanie się w tego typu akcję.


Btw. Co do relacji z 33 degree (kilka osób już o nią pytało), to musi ona jeszcze poczekać kilka dni, z powodu natłoku innych zajęć (w tym korekty ostatecznej wersji książki Tomka), jak i obecnych sporych zmian życiowych.

sobota, 4 lutego 2012

Testowanie wyjątków

Testowanie wyjątków w testach jednostkowych od zawsze trochę mnie irytowało. Niby sprawa banalna, ale jak do tego dołączymy słynny szablon // given // when // then to nie bardzo wiadomo gdzie powinniśmy umieścić słówko // then, aby test był dalej przejrzysty.

Problem nie jest nowy. Zastanawiano się nad dobrym rozwiązaniem już podczas prezentacji Bartosza Bańkowskiego i Szczepana Fabera pt. Pokochaj swoje testy [czas - 18.04] na Wroc JUG, albo pewnie jeszcze wcześniej. Idąc za radą lepszych, zawsze korzystałem z try / catch’a, aby wiedzieć gdzie dokładnie spodziewam się wyjątku.

@Test
public void shouldThrowSomeException() throws Exception {
    // given
    SomeClass someClass = new SomeClass();

    try {
        // when
        someClass.doSomething();
        fail("This method should throw SomeException");
    } catch(SomeException e) {
        // then
        assertThat(e.getMessage()).isEqualTo("Some message");
    }

}


Blok instrukcji try / catch wymusza na nas pewien sposób formatowania kodu i przez to nie do końca widać gdzie jest // when i // then. Można oczywiście próbować umieszczać je w trochę innym miejscu, np. // when przed try, a // then przed fail().

@Test
public void shouldThrowSomeException() throws Exception {
    // given
    SomeClass someClass = new SomeClass();

    // when
    try {
        someClass.doSomething();
        // then
        fail("This method should throw SomeException");
    } catch(SomeException e) {
        assertThat(e.getMessage()).isEqualTo("Some message");
    }

}

Jednak rozwiązanie dalej jest nie do końca czytelne. Gdyby jeszcze nic nie umieszczać w bloku catch, to już w ogóle, trzeba się chwilę zastanowić, co my tu tak na prawdę chcemy przetestować. Całe szczęście narzędzia do statycznej analizy kodu dbają o to, aby nie zostawiać pustych bloków catch.

Alternatywnym rozwiązaniem dla testowania rzucanych wyjątków, jest stosowanie andotacji @Test z parametrem expected w JUnit’cie  :

@Test(expected = SomeException.class)
public void shouldThrowSomeException() throws Exception {
    // given
    SomeClass someClass = new SomeClass();

    // when
    someClass.doSomething();

    // then
    fail("This method should throw SomeException");
}

Test wygląda już lepiej, ale ma swoje wady. Jak mi jakiś test nie przechodzi, to pierwsze co robię, to czytam sekcję // then testu. W tym przypadku widzę wywołanie fail() co sugeruje mi, że test zawsze powinien nie przechodzić. Dopiero po chwili zauważam, że test został zadeklarowany jako @Test(expected = SomeException.class), czyli spodziewam się wyjątku typu SomeException. Jest tutaj jednak pewne niebezpieczeństwo. Jeżeli faza // given testu, czyli przygotowania środowiska testowego, będzie trochę dłuższa, to może się zdarzyć, że tam gdzieś poleci wyjątek. Test będzie dalej przechodził, a tak naprawdę nie będzie testowane to co chcieliśmy. Wspominał o tym już Szczepan Faber w cytowanym fragmencie video. Dodatkowo nie można, jeśli byśmy chcieli sprawdzić np. treść komunikatu wyjątku. Z tych względów nie stosowałem tej konstrukcji.

Sprawa wygląda trochę  lepiej w przypadku TestNG. Tutaj mamy analogiczna adnotację, mianowicie zamiast expected używamy expectedExceptions.

@Test(expectedExceptions = SomeException.class,
        expectedExceptionsMessageRegExp = "Some Message.*")
public void shouldThrowSomeException() throws Exception {
    // given
    SomeClass someClass = new SomeClass();

    // when
    someClass.doSomething();

    // then
    fail("This method should throw SomeException");
}

Tu jeszcze dochodzi fajna zabawka w postaci expectedExceptionsMessageRegExp, czyli możemy za pomocą wyrażeń regularnych sprawdzić, czy wyjątek posiada spodziewaną wiadomość. Dalej jednak istnieje ryzyko wyrzucenia tego wyjątku w sekcji // given.

Podobną zabawkę daje nam JUnit, ale w trochę innym wydaniu. Mianowicie od wersji 4.7 można zastosować ExpectedException:

public class SomeClassTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void shouldThrowSomeException() throws Exception {
        // given
        thrown.expect(SomeException.class);
        thrown.expectMessage("Some Message");
        SomeClass someClass = new SomeClass();

        // when
        someClass.doSomething();

        // then
        fail("This method should throw SomeException");
    }
}

Tutaj w klasie testowej musimy zdefiniować regułę (linie 3 i 4), która początkowo mówi, że nie spodziewamy się wyjątków. Natomiast już w naszych metodach testowych, redefiniujemy to zachowanie i mówimy, czego się spodziewamy w danym teście (linie 9 i 10). Możemy dzięki temu sprawdzić komunikat rzucanego wyjątku. Tutaj jednak podajemy fragment wiadomości, którą ma zawierać nasz wyjątek. Istnieje również przeciążona wersja tej metody, która jako argument przyjmuje Matcher’a Hamcrest’owego.

Do niedawna były to jedyne rozwiązania, jakie były dostępne w temacie testowania wyjątku. Jakiś czas temu jednak pojawiła się ciekawa biblioteka: catch-exception. Kod napisany za jej pomocą może wyglądać tak:

@Test
public void shouldThrowSomeException() throws Exception {
    // given
    SomeClass someClass = new SomeClass();

    // when
    caughtException(someClass).doSomething();

    // then
    assertThat(caughtException())
            .isInstanceOf(SomeException.class)
            .as("Some Message");

}


Czyli mamy metodę CatchException.catchException(), gdzie jako argument przekazujemy obiekt naszej klasy. Następnie wywołujemy metodę, którą chcemy przetestować. Na koniec w sekcji // then sprawdzamy czy otrzymaliśmy wyjątek, którego się spodziewaliśmy. Bezargumentowa wersja caughtException() zwraca wyjątek rzucony przez ostatnią klasę, którą przekazaliśmy do metody caughtException(). W naszym wypadku jest to ostatni wyjątek wygenerowany przez someClass.

I to rozwiązanie mi się podoba. W sekcji // when informuję, że będę łapał wyjątki, a w sekcji // then sprawdzam czy poleciał ten wyjątek, którego oczekiwałem. I do tego nie bruździ to przy formatowaniu kodu i użyciu // given // when // then. I mamy czytelny kod :)

Zafascynowany tą biblioteką postanowiłem zajrzeć do środka (kod jest dostępny na googlecode.com), aby zobaczyć jak zbudowano takie rozwiązanie.

public class CatchException {

    public static <T> T catchException(T obj) {
        return processException(obj, Exception.class, false);
    }

}

Czyli mamy delegację pracy do metody processException(), która zwraca obiekt tego samego typu, jaki został przekazany w argumencie. Dzięki temu możemy używać biblioteki w sposób jaki pokazałem powyżej. Zobaczmy co kryje się za tą metodą:

private static <T, E extends Exception> T processException(T obj,
        Class<E> exceptionClazz, boolean assertException) {

    if (obj == null) {
        throw new IllegalArgumentException("obj must not be null");
    }

    return new SubclassProxyFactory().<T> createProxy(obj.getClass(),
            new ExceptionProcessingInterceptor<E>(obj, exceptionClazz,
                    assertException));

}

Po upewnieniu się, że argument nie jest null’em tworzymy (jak można się domyśleć po nazwach) proxy dla naszej klasy. Brnąc dalej w las, jeżeli klasa nie jest ani prymitywem, ani finalna, to proxy tworzone jest  w ten sposób:

proxy = (T) ClassImposterizer.INSTANCE.imposterise(
        interceptor, targetClass);

czyli wykorzystywany jest ExceptionProcessingInterceptor z poprzedniegu listingu, wewnątrz którego znajduje się następująca metoda, gdzie już widać całą magię:

public Object intercept(Object obj, Method method, Object[] args,
        MethodProxy proxy) throws Throwable {

    beforeInvocation();

    try {
        Object retval = proxy.invoke(target, args);
        return afterInvocation(retval);
    } catch (Exception e) {
        return afterInvocationThrowsException(e, method);
    }

}

Metoda beforeInvocation() czyści ostatnio złapany wyjątek, np. z wywołania poprzedniej metody. Następnie w bloku try wywoływana jest nasza rzeczywista metoda (linia 5), a następie zwracana jest (w naszym sposobie wykorzystania biblioteki) wartość wygenerowana przez oryginalną metodę. Jak coś pójdzie nie tak, to w bloku catch jest zapamiętywany rzucony wyjątek (zajmuje się tym metoda afterInvocationThrowsException()). Bardzo sprytny sposób na łapanie wyjątków, a jaki banalny zarazem.

Z ciekawostek jeszcze, to biblioteka korzysta z Mockito, a dokładniej cglib. Nie działa ona z klasami finalnymi (gdyż dla nich nie można teoretycznie tworzyć proxy), no chyba że skorzystamy w PowerMock’a i odpowiednich adnotacji w deklaracji klasy testowej:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ SomeClass.class })
public class SomeClassFinalPowerTest {
    // ...
}


Wtedy zadziała :)



Na koniec wpisu, jeszcze informacja skąd się dowiedziałem o tej bibliotece. Mianowicie powstaje teraz ciekawa książka o testach: Practical Unit Testing with TestNG and Mockito. Pisana jest ona przez Tomka Kaczanowskiego i już niedługo ujrzy światło dzienne. Książka poszła już do recenzji do Szczepana Fabra, więc lipy nie będzie. Będzie można się z niej dowiedzieć m.in. o catch-exception, jak i o testach mutacyjnych, o których pisałem ostatnio. Na razie wyjdzie wersja angielska, ale będzie też robione tłumaczenie na nasz ojczysty język. Zachęcam więc do śledzenia informacji o książce jak i do jej zakupu.

Więcej informacji o tym jak powstawała książka w kolejnych wpisach.

niedziela, 8 stycznia 2012

Testowanie mutacyjne z PIT Mutation Testing

Przeglądając dzisiaj (a właściwie to wczoraj) blogosferę natrafiłem na ciekawy post Tomka Kaczanowskiego First Glance At PIT Mutation Testing Tool na temat testowania mutacyjnego, za pomocą nowej biblioteki PIT Mutation Testing. Co to jest testowanie mutacyjne to można poczytać na Wikipedii: Testowanie mutacyjne (co ciekawe w tej chwili dostępny jest tylko artykuł w wersji angielskiej i polskiej).

PIT modyfikuje w locie nasz kod produkcyjny, puszcza testy i sprawdza, czy mutacje kodu są wykrywane przez nasze testy. Sprawdzamy dzięki temu jakość naszego kodu testowego, a dokładniej to, jak ściśle zdefiniowaliśmy zachowanie naszego kodu produkcyjnego za pomocą testów. Jest to trochę więcej niż pokrycie kodu testami wyrażone w procentach, gdyż raport ten podpowiada nam co jeszcze należało by przetestować.

Tomek stworzył przykładowy projekt i umieścił na github’ie. Ściągnąłem go, odpaliłem i przeglądnąłem raporty wygenerowane przez PIT’a. Jako że jest to prosty projekt na potrzeby testu tej biblioteki, postanowiłem zastosować go do swojego projektu MFCCChart. Co prawda nowej funkcjonalności do tego projektu już nie dodaję, ale się nim zabawiam testując jakieś ciekawe rozwiązania.

Patrząc na to co może PIT na stronie Mutation testing systems for Java compared w końcowej sekcji: Summary Of Mutation Testing Systems, to narzędzie to nie ma bezpośrednio wsparcia dla Ant’a, ale mamy interfejs linii komend. Przygotowałem więc komendę, dostosowaną do moich potrzeb:

java -cp out\test\MFCCChart;out\production\MFCCChart;testlib\junit-4.8.jar;testlib\mockito-all-1.8.5.jar;testlib\pitest-0.24.jar;lib\forms_rt.jar;lib\jcommon-1.0.16.jar;lib\jfreechart-1.0.13.jar org.pitest.mutationtest.MutationCoverageReport --outputFormats XML,HTML --reportDir reportspitests --targetClasses org.bitbucket.mstachniuk.mfccchart.* --targetTests org.bitbucket.mstachniuk.mfccchart.* --sourceDirs src --verbose --excludedMethods hasCode,equals --excludedClasses org.bitbucket.mstachniuk.mfccchart.view.*,org.bitbucket.mstachniuk.mfccchart.presenter.*

Najpierw mamy definicję naszego classpath’a. W katalogu out\test\MFCCChart mój ant’owy skrypt budujący wrzuca skompilowane testowe klasy, a do: out\production\MFCCChart klasy produkcyjne aplikacji. Następnie dołączyłem wszystkie wymagane do uruchomienia aplikacji jak i testów biblioteki. Klasa org.pitest.mutationtest.MutationCoverageReport jest klasą startową projektu PIT, a dalej mamy już argumenty dla tejże aplikacji. Ja zapragnąłem raportu w dwóch formatach (XML i HTML) w katalogu reportspitests. Następnie zapodałem klasy, które mają być mutowane, testowe klasy, kody źródłowe (aby można było zobaczyć gdzie wprowadzano mutacje). Jako że jest to biblioteka, której jeszcze nie znam to warto spojrzeć na szczegóły działania (opcja --verbose)  i wyłączyłem z testowania metody hasCode() i equals(), a także klasy z pakietów, które są odpowiedzialne za GUI i spinanie całości (do kupy).

Początkowo miałem problem z opcjami --outputFormats i --excludedClasses, gdyż nie chciały mi działać. Zgłosiłem nawet błąd na stronie projektu: Issue 23, ale szybko się okazało, że podczas kopiowania flag ze strony z Command Line Quick Start Notepad++ zamias zwykłego myślnika wstawił znak wyglądający niemal tak samo, ale o innym kodzie (najprawdopodobniej z po za zestawu ASCII).

Jak już się przekonałem, że generacja raportów działa, postanowiłem dorzucić tą analizę do skryptu budującego aplikację. Początkowo próbowałem to wykonać za pomocą taska exec ale nie chciało hulać. Po za tym task ten jest zależny od systemu operacyjnego i zarzuciłem go na rzecz taska java. I teraz poszło lepiej:

<target name="pitests">
    <java jvmargs="-Xmx600m" fork="true"
            classpath="${mfccchart.testoutput.dir};${mfccchart.output.dir};${basedir}/lib/jcommon-1.0.16.jar;${basedir}/lib/jfreechart-1.0.13.jar;${basedir}/lib/forms_rt.jar;${basedir}/testlib/junit-4.8.jar;${basedir}/testlib/mockito-all-1.8.5.jar;${basedir}/testlib/pitest-0.24.jar"
            classname="org.pitest.mutationtest.MutationCoverageReport"
            args="--reportDir reportspitests --targetClasses org.bitbucket.mstachniuk.mfccchart.* --targetTests org.bitbucket.mstachniuk.mfccchart.* --sourceDirs src --verbose --excludedMethods hasCode,equals --excludedClasses org.bitbucket.mstachniuk.mfccchart.view.*,org.bitbucket.mstachniuk.mfccchart.presenter.* --outputFormats XML,HTML">
    </java>
</target>


Bez fork'a nie chciało mi działać (zresztą pewnie jak większość tego typu rozszerzeń wywoływanych z Ant'a) Co mogłem to pozamieniałem na ścieżki zdefiniowane w pozostałej części skryptu. Jeszcze podaję sporą ilość ścieżek do konkretnych bibliotek co mi się nie podoba. Podejrzewam, że jest lepszy sposób na to ale mistrzem Ant’a nie jestem. Jak by ktoś wiedział, to proszę o komentarz.

Teraz czas na analizę raportu: „Detected 84 of 93 mutations”. Czyli na 9 różnych mutacji podatny jest mój kod. Patrząc na pokrycie klasowy, to chyba bardzo dobrze to wypadło:

Mutated classesLine coverageMutation coverage
OneMfccFileStatistic100%85%
MfccFileReader 100%97%
FrameInformation 100% 100% 
ChartSettings 100% 100% 
MfccFrame 100% 100% 
StatisticTableModel 100% 80% 

Ja jestem zadowolony z wyniku. Jak ktoś chce to niech sobie przejrzy mój przykładowy raport: MFCCChart_pit_reports_201201080158.zip

Teraz pozostaje tylko rozkminienie czego ode mnie chce ten raport (czyli co jeszcze mogę poprawić w swoich testach), jak i przyjrzenie się konkurencyjnym rozwiązaniom, których zestawienie można obejrzeć na wspominanej już stronie: Mutation testing systems for Java compared.

poniedziałek, 12 grudnia 2011

Agile Development Day

W sobotę 10 grudnia odbył się w Warszawie Agile Development Day. Była to impreza zorganizowana przez firmy Sages i Pragmatis. Event polegał na wspólnym tworzeniu aplikacji, ćwiczeniu TDD, programowania w parach i innych zwinnych praktykach. Był do tego Continous Integration (build był uruchamiany co git push) i Continous Delivery (deployment co udany build).

Aby dostać się na warsztaty trzeba było się wcześniej zapisać. Z ponad 60 osób chętnych, wybrano 22 w tym mnie :) Została przygotowana startowa baza kodu i udostępniona na github’ie. Trzeba było więc wcześniej ściągnąć kod i przygotować środowisko. Technologicznie był Spring Core, Spring MVC, JSP, Casandra, Maven. Jako że nie miałem wcześniej możliwości korzystać ze Spring’a w warunkach bojowych (po za jakimiś szkoleniami / prezentacjami), to miałem z czym się bawić. Zaprzyjaźniłem się więc z książkami na ten temat i archetypami kodu, aby porozkminiać te technologie i aby było na czym bazować.

Event zaczął się o 8 rano w Millennium Plaza. Najpierw było przywitanie i omówienie zasad, co będziemy robić w ciągu dnia. Następnie było o aplikacji jaką mamy stworzyć. Mianowicie za pomocą pisanego serwisu internetowego można się chwalić czego się nauczyliśmy, ile czasu na to poświeciliśmy i ile punktów zebraliśmy. Taki jakby twitter, tyle że piszemy co poznaliśmy, jaki był stopień trudności, system nalicza jakieś tam punkty i chwalimy się tym przed znajomymi. Plus jeszcze jakieś pierdoły do tego.

Po omówieniu wymagań aplikacji było wprowadzenie do Casandry, gdyż była to najbardziej „dziwna” i nieznana technologia z używanego technology stack. Później było szybciutko o Spring MVC i zaczęło się. Uczestnicy mieli wybrać sobie couch’a i osobę do pary (najlepiej aby ktoś znał Springa MVC). Jako że znałem Piotra (jednego z prowadzących) to się z nim dogadałem i wraz z Marcinem usiedliśmy do pierwszego user story.

Wybraliśmy sobie na tapetę formatowanie czasu, ile zajęła nam nauka danej technologii. Napisaliśmy kilka testów i zaczęła się zabawa. Postanowiliśmy dodać do projektu bibliotekę JUnitParams do pisania sparametryzowanych testów w JUnit’cie. Swoją drogą jest to biblioteka napisana przez Pawła Lipińskiego, który organizował, prowadził i kodował na warsztatach. Pomogło nam to pisać ładniejsze, parametryzowane testy w JUnicie, podczas pierwszej iteracji.

Wcześniej ustalono, że iteracje będą trwały po półtorej godziny i pomiędzy nimi będą zmiany par, a co kilka iteracji będzie retrospekcja. Czyli jakby jeden dzień ze sprintu został skompresowany do 1,5h a cały Sprint do 5ciu dni (czyli pięciu iteracji). Taka organizacja pracy bardzo motywowała do jak najszybszego kończenia zadań. Nie inaczej było w naszej parze, gdyż chcieliśmy jak najszybciej zobaczyć choćby częściowy efekt naszej pracy na „live”, mimo że nie wszystkie przypadki mieliśmy obsłużone. No i tu zaczęła się prawdziwa walka z technologiami.

Gdy jeszcze wczoraj Piotrek w projekcie dodał Tiles’y zaczęła się cała aplikacja wysypywać. Doszedł on do wniosku, że Casandra z Tiles’ami nie funkcjonuje ;) i wycofał zmiany. W naszej historyjce chcieliśmy (wręcz musieliśmy) skorzystać z JSTL’owego Taga c:forEach, ale też nie chciało działać. Był ten sam błąd co w przypadku Tiles’ów. Nasza koncepcja zamknięcia wyświetlania czasu we własny Tag JSTL’owy również nie zadziałała i trzeba było to jakoś inaczej obmyślić. To wszystko pewnie przez Casandrę ;)

Akurat nadszedł koniec pierwszej iteracji i nastąpiła rotacja par. Jako, ze pracowaliśmy na moim laptopie, to Marcin odszedł, a dołączył się Michał. Porozmawialiśmy chwilę, co zrobić z naszym problemem niemożliwości użycia JSTL’a i zaproponowałem, aby stworzyć Data Transfer Object (DTO) i za jego pomocą wyświetlać już odpowiednio sformatowany tekst. Coś tam niby się udało, ale potem było trochę walki z merge’owaniem zmian. Niestety szybko się okazało, że user story bardzo nachodzą na siebie, wręcz się powtarzają. Powodowało to masę konfliktów i trudności z push’owaniem zmian, gdyż po pull’u, odpaleniu testów, i przy próbie ponownego push’a okazywało się, że ktoś w między czasie zdążył wypushować swoje zmiany i proces trzeba było powtarzać ponownie.

Po drugiej iteracji postanowiono zrobić retrospekcję. Mieliśmy na małych, przylepnych karteczkach wypisać, co nam się podobało, a co nie i przykleić to na ścianę. Później trzeba było w ciszy to pogrupować (silent sorting) i ponazywać grupy. Po „uroczystym” przeczytaniu tego co jest fajne i niefajne, każda para wraz ze swoim mentorem miała się zastanowić, co zrobić, aby było lepiej. Super pomysłem było wprowadzenie stand-up meeting’ów - w trakcie trwania iteracji - [chyba] co 20 minut, aby poprawić komunikację w zespole i wiedzieć, kto co robi i na kiedy będzie. Innym ciekawym pomysłem było wpisywanie podczas commit'a / push’a jako autor numer implementowanego user story i nazwiska autorów. I rzeczywiście, początkowo brakło konwencji wypychania zmian do repozytorium, aby były one jednolite i czytelne.

Następnie był obiad i kolejna iteracja. Tym razem osoby, które przez dwie iteracje robiły jedną opowieść, miały się przesiąść. Może to trochę nieoptymalne posunięcie z punktu widzenia ukończenia zadania, ale podczas eventu chodziło o wspólną naukę. Trafiłem więc do zadania wyświetlania podpowiedzi. Mianowicie gdy wpisujemy technologię, której się właśnie nauczyliśmy, ma nam podpowiadać czy było to łatwe, średnie czy trudne. Wiązało się to z koniecznością napisania kawałka JavaScript’a, ale na szczęście to akurat poszło gładko. Problemem się jednak okazało, gdy coś co było zależne od naszego zadania (mianowicie dodawanie nowych umiejętności) czy jest już skończone i czy dane lądują tam, skąd je odczytujemy. Niestety ciężko w Casandrze cokolwiek podejrzeć, co wylądowało w bazie danych, gdyż zapisują się tam dane binarne. Nad tym zadaniem pracowałem przez dwie iteracje i później na ostatnią iterację, znów trzeba było się przesiąść.

Dosiadłem się więc do Bartka i tam nawet początkowo było fajnie (nie znaczy to wcale, że wcześniej było źle). Dostałem szybkie wprowadzenie co robimy, że właśnie jest test z nową funkcjonalnością i mam ją zaimplementować. Chwila ogarniania / czytania kodu i z pomocą Bartka udało się. Problem niemożliwości skorzystania z JSTL’a rozwiązano użyciem Skryptletów. Coś tam nawet się udało wyświetlić, ale nie można było wypchać zmian, gdyż najpierw trzeba było powalczyć  z kwestią, co wyświetlać, jak ktoś jest niezalogowany. Inny zespół pisał back-end do tej funkcjonalności i niestety czas warsztatów nie pozwolił już na połączenie naszych zmian.

Po piątej iteracji przejrzeliśmy backlog zapisywany na tinypm.com. Okazało się, że niewiele funkcjonalności udało się w pełni zaimplementować. Paweł pokazał jednak na żywym środowisku, że coś tam działa. Patrząc na statystyki na Jenkinsie, było widać, że w ciągu zmian udało się wypchnąć do repozytorium kod 54 razy z czego tylko 9 się wywaliło. Uważam to za niezły wynik, biorąc pod uwagę to, że dla uczestników był to świeży projekt, bez wypracowanych konwencji i nie każdy musiał znać wszystkie wykorzystywane technologie. Poniżej przedstawiam trend tesów.


Podczas warsztatów budowały się build’y od 88 do 142. Wcześniejsze build’y pochodziły z przygotowań do warsztatów. Widać wyraźnie że od 95 build’a liczba testów zaczęła znacząco rosnąć, z każdą nową zmianą.

Na koniec nadszedł czas na podsumowanie. Poprzestawialiśmy trochę stoły i ułożyliśmy krzesła w dwa kółka. W środku było 5 krzeseł, a  na zewnątrz reszta. Kto siedział w środku ten mógł mówić, pod warunkiem, że cztery wewnętrzne krzesła były zajęte. Jedno było wolne, aby kogoś mógł się dosiąść i oczywiście można było spokojnie opuścić wewnętrzny krąg. Osoby siedzące w środku mówiły o swoich odczuciach dotyczących warsztatów, co się nauczyły itd. Czyli taka runda feedback’owa dla innych uczestników i organizatorów.

Jako że ja nie miałem okazji zasiąść w środku, to swoją opinię wyrażę teraz. Ogółem event wyszedł bardzo fajnie. Organizacja w porządku: były kanapki rano, napoje i ciasteczka przez caly dzień, dobry obiad w trakcie (i nie była to pizza). Lokalizacja warsztatów bardzo dobra, mianowicie blisko centralnego – ważna kwestia dla osób przyjezdnych, a tych nie brakowało. Ja sam przyjechałem z Wrocławia, ale spotkałem również ludzi z Gdyni, Gdańska, Wrocławia, a nawet z Berlina. Organizacja pod względem technicznym również dobra – były dwa rzutniki, na jednym Jenkins, a na drugim live aplikacja. Bardzo dobrym pomysłem było przygotowanie zaczynu aplikacji i jej udostępnieniu w necie. Również Continous Integration i Delivery daje bardzo fajny efekt psychologiczny. Można było szybko zobaczyć swoje zmiany na Jenkinsie jak i na na środowisku „produkcyjnym”.

Niestety, jak to sami organizatorzy przyznali, rozpoczęcie przygotowywania tej aplikacji na tydzień przed warsztatami to było trochę za późno. Wynikały później z tego problemy z technologiami (niemożliwość użycia Tiles’ów i JSTL’a). Uważam również, że dobranie stosu technologicznego było nienajszczęśliwsze. Fajnie, że było cos egzotycznego (Casandra), ale mało kto wiedział, jak z tego odpowiednio korzystać. Do tego zastosowanie Springa MVC i JSP powodowało, że czasem trzeba było się męczyć z JavaScriptem (AJAX i jQuery). Te problemy technologiczne powodowały, ze ciężko było się skupić na czystym pisaniu kodu (nie wspominając już o TDD), a trzeba było się męczyć ze zgraniem technologii i merge’owaniem zmian.

Wcześniej wydawało mi się że w miarę ogarnąłem już Git’a, ale dopiero tutaj w warunkach bojowych zweryfikowałem swoje poglądy na ten temat. Problem scalania zmian występuje (pushowaliśmy do jednej gałęzi, aby Continous Server już dalej robił swoje), jak w każdym innym systemie kontroli wersji. Jakub Nabrdalik mówił, że jednym z możliwych sposobów jest zrobienie miecza z dwóch zwiniętych kartek papieru i ten kto aktualnie posiada miecz może push’ować. Jak ktoś innyc chce to robić, to musi uzyskać ten miecz. Wydaje mi się, że skorzystanie z Mercuriala mogło by trochę ułatwić sprawę z łączeniem zmian w kodzie, ale nie jestem ekspertem w tym temacie.

Innym poważnym minusem co do warsztatów, było przygotowanie user story’s. Bardzo często były one do siebie podobne, zachodziły na siebie i od siebie zależały. Jednak podczas wyboru user story nie posiadaliśmy tej wiedzy. Na przyszłość trzeba lepiej przygotować historyjki do zaimplementowania, wraz z grafem, co od czego zależy, co trzeba zrobić najpierw i z lepszym opisem. Większość opowiastek dotyczyła strony głównej, przez co wzajemnie sobie modyfikowaliśmy zawartość tych samych plików, co prowadziło do problematycznych konfliktów. Na przyszłość można kilka opowiastek udostępnić np. tydzień wcześniej, aby można było w domu coś na rozgrzewkę napisać. Zachęciło by to bardziej do zajrzenia w kod i do lepszego przygotowania do warsztatów. I najlepiej aby były one możliwie jak najbardziej rozłączne.

Również wybór Casandry na bazę danych nie było chyba najlepsze, gdyż z rozmów pomiędzy ludźmi słyszałem, że niektóre zespoły sporo się z nią męczyły. Fajnie poznać przy takiej okazji coś nowego, ale wybór chyba nie był najszęśliwszy. Jak bym proponował na następny raz jakąś bazę działającą w pamięci, inną NoSQL’ową (obiektową, key-value) lub jakąś lekką SQLową, którą wszyscy znają. Jako technologię widoku proponowałbym GWT (lub coś o podobnej koncepcji), gdyż tam nie trzeba się bawić z JavaScriptem, ani z JSP, a maksymalnie z niewielkimi kawałkami HTML’a.

Nie oznacza to, ze nic nie wyniosłem z warsztatów. Miałem w końcu motywację do lepszego poznania Springa, Git’a, JUnitParams… Zrozumiałem, że nie tylko testy jednostkowe są najważniejsze, ale obok nich musi się znajdować kilka integracyjnych i akceptacyjnych (już w początkowym stadium projektu). Przykładowo prosty test end-to-end uruchamiający przeglądarkę na stronie głównej i sprawdzający czy jakiś tam przycisk istnieje, pozwolił nam szybko wykryć, że Tagi w JSP nie chcą działać. Inna sprawa ze testy nie były podzielone na jednostokowe i inne (trwające dłużej), przez co ich uruchamianie było trochę przydługie.

Dowiedziałem się też o fajnej technice, podkreślającej współwłasność kod. Mianowicie po za wywaleniem z szablonów plików / klas i metod informacji o autorze, można dać wszystkim pracownikom z danego projektu, jedno konto i hasło do repozytorium. Dopiero wtedy powstaje wspólny kod i nikt się nie boi modyfikować nieswoich części. Poznałem również trochę bardziej w praktyce procesy zachodzące w procesie Agile'owym, jak i mogłem się dowiedzieć jak wygląda IT w innych miastach.

Zawsze z takich wydarzeń można wynieść ciekawe skróty klawiaturowe. I tak dla IDEI: Crtl + Shift + Enter - Statement Comlete, czyli dodanie średnika na końcu linii. Z analogicznych skrotów korzystałem w NetBeans’ie, ale tam to było to Ctrl + ; i Ctrl + Shift + ;. Innymi interesującymi skrótami w Idei są: Ctrl + Shift + T czyli przejście do klasy, która testuję aktualną klasę, w której jesteśmy i Ctrl + Shift + N (odpowiednik Eclipsowego Ctrl + Shift + R), czyli wyszukiwanie po nazwie pliku z projektu.

Ciekawe, nowopoznane skróty dla Eclipse’a to Ctrl + Shift + M - zaimportowanie statyczne danej metody na której znajduje się aktualnie focus. Przydatne zwłaszcza jak mockujemy z Mockito i chcemy samo when() zamiast Mockito.when(). Ponadto Ctrl + Shift + O czyli organizacja importów (w Idei można zrobić, aby automatycznie narzędzie przed commitem zmian, odpowiednio organizowało importy). I na koniec syso + Ctrl + Spacja, czyli skrót od System.out.println();. W Idei jest to sout + Tab co jest według mojego gustu fajniejsze.

Dobra to chyba tyle z podsumowania wydarzenia. W tym roku już się żadna konferencja / warsztaty nie szykują, więc trzeba będzie z tym poczekać do przyszłego roku. Mam nadzieję na kolejną edycję Agile Development Day lub podobny tego typu event.

niedziela, 4 grudnia 2011

I po Global Day of Code Retreat 2011 w Krakowie


W sobotę 3ciego grudnia miało miejsce ciekawe wydarzenie: Global Day of CodeRetreat. Czym jest samo CodeRetreat można poczytać w moim wpisie z przed ponad roku: Wrażenia po CodeRetreat we Wrocławiu. Kilka miast przypadkiem zaczęło organizować u siebie tego typu imprezę akurat 3ciego grudnia. Postanowiono więc, tą datę ochrzcić Globalnym Dniem CodeRetreat’a i zaczęto masowo organizować to wydarzenie na całym świecie. Kraków był jednym z Polskich miast (zaraz obok Łodzi, Poznania i Warszawy), który zorganizował „Rekolekcje Kodu” i postanowiłem się tam wybrać.

Miałem okazję pokodować ze znajomymi jak i z kompletnie obcymi osobami. Zdziwiło mnie, że mimo iż wielokrotnie podchodziłem do problemu Gry w życie Conwaya (na poprzednich CR’ach jak i w domu), to i tak poznałem kolejne, nowe sposoby, jak można ugryźć problem. W wolnej chwili trzeba będzie jeszcze sprawdzić kilka rozwiązań.

Podczas wydarzenia udało mi się m.in. wraz z Tomkiem Kaczanowskim nie używać w ogóle myszki. Po prostu ją wyłączyliśmy. Trochę touchpad w laptopie przeszkadzał (muszę w końcu poszukać, czy da się go jakoś wyłączyć) ale udało się. Trochę innych skrótów klawiaturowych używaliśmy, dzięki czemu można było się „wymienić” znanymi hotkey’ami.

Miałem również okazję popisać w C# z którego na co dzień nie korzystam. W tym języku pozytywnie zaskoczył mnie LINQu po tym, jak spory kawałek brzydkiego kodu z zagłębionymi wcięciami został jednym skrótem klawiaturowym zamieniony na 2 linijki kodu! Był on czytelny, przypominał Fluent Interface i dało się go zrozumieć. Nie wiem czy jest dostępny mechanizm odwrotny – na pewno byłby przydatny dla poczatkujących programistów i chcących poznać ten język.

Ciekawą techniką okazało się również TDD as if you meant it czyli pisanie całego kodu w teście i dopiero na końcu jego ekstrahowanie do metod i osobnych klas na zewnątrz. Ciekawe podejście, podczas którego trzeba nagiąć nasze przyzwyczajenia i pisać kod produkcyjny w metodzie testowej. Niebezpieczeństwem tej metody jest rzecz o której ktoś na sali wspominał, że testy były zielone, ale kod był dalej w testach. Więcej informacji odnośnie tej techniki: na coderetreat.orgcumulative-hypotheses.org i gojko.net.

Miałem również okazję poćwiczyć ping pong’a, gdzie piszemy test dla drugiej osoby, a ta wykonuje implementację, refaktoring i test dla nas. Moje osobiste odczucie jest takie, że staramy się wtedy „odbić” piłeczkę tak, aby drugiemu ciężko było ją „odebrać”. Prowadzi to często do tego, że jedna osoba spędza większość czasu przed klawiaturą, a druga staje się mniej aktywna. No i mogą powstawać wtedy potworki. Przykład poniżej:



Kasowałem kod po każdej z sesji, ale tego screen’a musiałem zrobić :) Co fajniejsze, w tym kodzie jest błąd. Na szczęście udało się go znaleźć, ale na refaktoryzację już zabrakło czasu.

Kolejnym pozytywnym zaskoczeniem dla mnie było to, że coraz więcej osób korzysta / chce korzystać z IntelliJ Idea. Ludzie albo mają dość Eclipse’a, który czasami zachowuje się niedeterministycznie, albo zauważają potęgę IDEI. Żeby tylko jeszcze pracodawcy chętnie w nią inwestowali...

W trakcie jednej z przerw łączyliśmy się przez Skype’a z Nowym Yorkiem i Łodzią, gdzie również odbywało się CodeRetreat. Można było chwilę pożartować i poczuć, że jest to to globalne wydarzenie i że gdzieś indziej ludzie również bawią się wspólnie kodujac. Ogółem do zabawy przyłączyło się 90 miast i około 2200 developerów.

Organizacyjne event w Krakowie był dobrze przygotowany. Mi zabrakło informacji, że tak naprawdę zaczynamy od godziny 9 a nie od 8. Zawsze to by człowiek chwilę dłużej pospał, zwłaszcza gdy się wyjeżdża o 5tej rano z Wrocławia. Niewyspanie dawało chwilami o sobie znać.

Obiad był i nie była to pizza, więc wymaganie podstawowe warsztatów zostało zachowane. Nie był to też obiad w stylu restauracyjnym, ale za to można było wziąć dokładkę. Szybko jednak zabrakło soków i w ogóle nie było herbaty (o dziwo nie wszyscy piją kawę). Początkowo panował mały organizacyjny chaos, ale po 9 godzinie szło już sprawnie. Osobiście trochę nie podobały mi się pytania na retrospektywie (w stylu ile osób robiło coś tam), gdyż to nie zachęca do opisywania swoich odczuć z danej sesji. Ogólne pytanie w stylu: „jak odczucia?” sprawdziło by się lepiej. We Wrocławiu rok temu działało.

Ogólnie całe wydarzenie wypadło bardzo pozytywnie. Widać, ze coraz szerzej techniki programowania w parach jak i TDD są znane wśród programistów. Motywuje to do dalszego działania i zgłębiania tematu.

czwartek, 1 grudnia 2011

Malowanie wykresów za pomocą JFreeChart cz. 2

Ostatnim razem we wpisie Malowanie wykresów za pomocą JFreeChart cz. 1 pokazałem jak przygotować prosty wykres za pomocą biblioteki JFreeChart. Teraz chciałbym rozszerzyć przykład, poprzez pokazanie, jak dodać do wykresu własną skalę kolorów.

Aby to zrobić musimy stworzyć klasę implementującą interfejs PaintScale. Definiuje on dwie metody: getLowerBound() i getUpperBound(). Powinny one zwracać odpowiednio dolną i górną granicę skali. Trzecią i najważniejszą metodą jest getPaint(). Zwraca ona obiekt typu Paint. Początkowo chwilę się zastanawiałem, co to za twór, co sobą reprezentuje i jak go utworzyć…

Na szczęście klasa Color z tej samej biblioteki implementuje ten interfejs. Dzięki temu możemy w naszej implementacji zwrócić odpowiedni kolor. Poniżej przykładowa implementacja.

public class ColorPaintScale implements PaintScale {
    private static final Color[] colors = {
            new Color(36, 35, 105),
            new Color(0, 6, 252),
            new Color(0, 134, 250),
            new Color(9, 251, 242),
            new Color(135, 252, 112),
            new Color(254, 241, 3),
            new Color(255, 117, 0),
            new Color(244, 0, 1),
            new Color(104, 21, 21),
    };

    private int lowerBound = 0;
    private int upperBound = 100;

    public ColorPaintScale(int lowerBound, int upperBound) {
        this.lowerBound = lowerBound;
        this.upperBound = upperBound;
    }

    public double getLowerBound() {
        return lowerBound;
    }

    public double getUpperBound() {
        return upperBound;
    }

    public Paint getPaint(double v) {
        double divisor = (upperBound - lowerBound) / colors.length;
        int index = (int) ((v - lowerBound) / divisor);
        if (index < 0) {
            return colors[0];
        }
        if (index >= colors.length) {
            return colors[colors.length - 1];
        }
        return colors[index];
    }
}

W powyższej klasie na początku definiujemy sobie kolory, jakie mają być wyświetlane na naszej skali. W liniach 14 i 15 definiujemy dolną i górną wartość naszej skali. Pola te są inicjowane w konstruktorze. Następnie mamy gettery dla tych pól (zdefiniowane w interfejsie) i metodę getPaint() do zwracania odpowiedniego koloru z dostępnej skali.

Gdy już mamy naszą klasę odpowiedzialną za skalę, przekazujemy ją do obiektu renderer’a:

PaintScale scale = new ColorPaintScale(0, 10);
XYBlockRenderer renderer = new XYBlockRenderer();
renderer.setPaintScale(scale);

Poniższy kod można zastosować w ostatnim przykładzie z poprzedniego postu. Kod i efekt poniżej:

public JPanel chartWithScale() {
    DefaultXYZDataset xyzDataset = new DefaultXYZDataset();
    double[][] series01 = new double[][]{{1}, {1}, {5}};
    double[][] series02 = new double[][]{{2, 5}, {3, 4}, {7, 9}};
    xyzDataset.addSeries("Series01", series01);
    xyzDataset.addSeries("Series02", series02);

    NumberAxis domainAxis = new NumberAxis("X Label");
    domainAxis.setRange(0, 10);

    NumberAxis valueAxis = new NumberAxis("Y Label");
    valueAxis.setRange(0, 7);

    PaintScale scale = new ColorPaintScale(0, 10);
    XYBlockRenderer renderer = new XYBlockRenderer();
    renderer.setPaintScale(scale);

    XYPlot plot = new XYPlot(xyzDataset, domainAxis, 
            valueAxis, renderer);

    JFreeChart chart = new JFreeChart("title", plot);

    return new ChartPanel(chart);
}



Ok, pola zostały pomalowane na kolorowo, ale dalej nie widzimy skali. Aby to zrobić musimy mieć utworzony już obiekt typu Chart, gdyż do niego będzie dodawać "podtytuł". Napiszmy zatem do tego osobną metodę:

private void addPaintScaleToChart(PaintScale scale, JFreeChart chart) {
    NumberAxis numberAxisPaintScale = new NumberAxis("Skala");
    numberAxisPaintScale.setTickLabelFont(new Font("Dialog", 0, 7));

    PaintScaleLegend paintScaleLegend = new PaintScaleLegend(scale, 
            numberAxisPaintScale);
    paintScaleLegend.setAxisOffset(5D);
    paintScaleLegend.setMargin(new RectangleInsets(5D, 5D, 5D, 5D));
    paintScaleLegend.setPadding(new RectangleInsets(10D, 10D, 
            10D, 10D));
    paintScaleLegend.setFrame(new BlockBorder(Color.RED));
    paintScaleLegend.setStripWidth(10D);
    paintScaleLegend.setPosition(RectangleEdge.RIGHT);
    chart.addSubtitle(paintScaleLegend);
}

I ją wywołajmy po utworzeniu obiektu chart. Efekt poniżej:



Na początku przedstawianej metody addPaintScaleToChart() tworzymy skalę i ustawiamy jej czcionkę. Jakbyśmy chcieli się pozbyć czarnych poziomych i pionowych kreseczek pomiędzy liczbami a kolorami, możemy wywołać poniższe dwie metody:

numberAxisPaintScale.setAxisLinePaint(Color.white);
numberAxisPaintScale.setTickMarkPaint(Color.white);

Następnie tworzymy legendę i ustalamy kilka właściwości. Za pomocą setAxisOffset() ustawiamy odległość pomiędzy podziałką a kolorową skalą. Poprzez setMargin() ustawiamy otoczenie naszej skali na panelu z wykresem. Nazwa argumentu sugeruje działanie podobne do Swingowych (czy też AWTowych) Insets, aczkolwiek klasy te są ze sobą nie powiązane. Za pomocą setPadding() ustawiamy odstęp we wnętrzu naszej skali, tzn. odległości kolorowej skali od brzegu legendy.

Dalej ustawiamy kolor obramowania, szerokość kolorków i pozycję względem wykresu. Na koniec dodajemy naszą legendę do wykresu za pomocą addSubtitle().

To chyba tyle w temacie biblioteki JFreeChart. Na razie nie planuję dalszej zabawy z nią, a w kolejce czekają następne tematy do ogarnięcia.

poniedziałek, 28 listopada 2011

Malowanie wykresów za pomocą JFreeChart cz. 1


Jakiś czas temu „pracując” przy małym projekciku, malowałem sobie ciekawe rysuneczki w po ekranie. Mianowicie bawiłem się biblioteka JFreeChart, która to potrafi w całkiem przystępny sposób malować wykresy w Javie.

Na początku, co bardzo się rzuca w oczy, to ciekawy sposób dystrybucji (czy też modelu biznesowego) tejże biblioteki. Jest ona w zupełności darmowa („is a free 100%”), sa Javadoc’i, jest aplikacja pokazująca możliwości biblioteki, ale chcąc poczytać The JFreeChart Developer Guide trzeba zapłacić. Bardzo ciekawe rozwiązanie, zwłaszcza, że udostępniane Javadoc’i są trochę ułomne.

Na szczęście jest kilka przykładów w Necie np. na stronie java2s.com. Po za tym ten kto jest kumaty, potrafi sobie zdekompilować udostępniane sample i zobaczyć co siedzi w środku :D Czyli dostać to samo co nam daje nam płatny developer guide.

Na początek zróbmy więc coś prostego. Załóżmy, że chcemy zrobić wykres słupkowy (ang. Bar Chart). Utwórzmy więc najpierw obiekt przechowujący nasze dane:

DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset();

Wszystkie zbiory danych (ang. dataset) są reprezentowane za pomocą klas implementujących interfejs Dataset. Interfejs ten jest bardzo ogólny i nie korzystamy z niego bezpośrednio. Chcąc stworzyć pewien wykres, musimy się dowiedzieć, jakiego typu zbioru danych on wymaga. I tak wykres słupkowy, potrzebuje IntervalXYDataset. Za pomocą tego interfejsu, klasa tworząca wykres, będzie wiedziała, jak się odwołać do danych w nim zawartych. Interfejs nie definiuje jednak sposobu dodawania tych danych do zbioru, dlatego musimy skorzystać z konkretnej implementacji przy deklaracji (po lewej stronie znaku równości) tej zmiennej.

Następnie przydało by się wprowadzić jakieś dane do zbioru:

double[][] series01 = new double[][] {{2}, {1}, {3}, {7}, {4}, {5}};
dataset.addSeries("Series 1", series01);

W pierwszej linijce tworzymy tablicę, która musi posiadać 6 wierszy, każdy o takiej samej długości, np. o długości jeden. Jeśli będziemy mieć inną liczbę wierszy otrzymamy IllegalArgumentException z jasnym komunikatem: The 'data' array must have length == 6. Wiersze oznaczają kolejno: x, start-x, end-x, y, start-y i end-y. Nasz wykres korzysta tylko z pierwszych 4rech. X oznacza wartość, dla jakiej definiujemy „wysokość” słupka. Wartość ta może być wyświetlana, gdy najedziemy myszką na ten słupek. Następnie start-x i end-x oznaczają, gdzie ma się zaczynać i kończyć słupek na osi X. Aby wykres był czytelny, wartość x powinna być pomiędzy wartościami start-x i end-x, najlepiej w połowie. Y oznacza wartość, jak wysoki ma być słupek, a start-y i end-y nie są używane, gdyż słupki z założenia na tym wykresie są prostokątne, a nie trapezowate.

Gdybyśmy chcieli wyświetlić kilka słupków dla jednej serii, to wówczas nasze wiersze w tablicy będą dłuższe, ale każdy wiersz musi zawierać dokładnie taką samą ilość liczb.

W drugiej linijce powyższego listingu dodajemy serię do naszego zbioru danych. Należy pamiętać aby użyć unikatowego identyfikatora w ramach zbioru, bo inaczej zobaczymy tylko najnowsze / nadpisane serie.

Następnie tworzymy wykres. Najłatwiej na początku skorzystać z metody fabrykującej dostarczanej przez ChartFactory:

JFreeChart chart = ChartFactory.createXYBarChart("title", "x Label", 
        false, "y Label", dataset, PlotOrientation.VERTICAL, true, 
        true, false);

Jako argumenty podajemy m.in. tytuł wykresu, podpisy osi, zbiór danych, orientację wykresu i jeszcze kilka flag. Trzeci argument mówi czy osi X ma być skalą związana z datami. My takiej nie chcemy. Trzy ostatnie argumenty definiują nam kolejno, czy chcemy legendę, podpowiedzi i adresy URL.

Na koniec trzeba jeszcze stworzyć instancję klasy ChartPanel opakowującą nasz wykres. Dzięki tej klasie możemy już wykres umieścić w JPanel’u Swingowym. Całościowy przykładowy kod i efekt poniżej:

DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset();
double[][] series01 = new double[][] {{2, 22}, {1, 21}, {3, 23}, 
        {7, 27}, {4, 24}, {5, 25}};
double[][] series02 = new double[][]{{5}, {4}, {8}, {13}, {20}, {25}};
dataset.addSeries("Series 1", series01);
dataset.addSeries("Series 2", series02);

JFreeChart chart = ChartFactory.createXYBarChart("title", "x Label", 
        false, "y Label", dataset, PlotOrientation.VERTICAL, true,
        true, false);

JPanel jPanel = new ChartPanel(chart);



Podobne demo, opisujące jak namalować wykres słupkowy możecie zobaczyć na stronie JFreeChart: XY Bar Chart Demo na http://www.java2s.com

Dobra to tyle na początek, przejdźmy do bardziej zaawansowanych zagadnień. Za zadanie miałem stworzyć dość nietypowy wykres, coś takiego:



Kolor danego „kwadracika” był jakoś tam obliczany dla danego X i Y – nie jest to przedmiotem tego wpisu. Mianowicie im wartość Z (przypisana danemu X i Y) mniejsza, tym kolor „zimniejszy”. Spróbujmy więc co podobnego namalować:

DefaultXYZDataset xyzDataset = new DefaultXYZDataset();
double[][] series01 = new double[][]{{1}, {1}, {5}};
double[][] series02 = new double[][]{{2, 5}, {3, 4}, {8, 9}};
xyzDataset.addSeries("Series01", series01);
xyzDataset.addSeries("Series02", series02);

NumberAxis domainAxis = new NumberAxis("X Label");
domainAxis.setRange(0, 10);

NumberAxis valueAxis = new NumberAxis("Y Label");
valueAxis.setRange(0, 7);

PaintScale scale = new GrayPaintScale(0, 10);
XYBlockRenderer renderer = new XYBlockRenderer();
renderer.setPaintScale(scale);

XYPlot plot = new XYPlot(xyzDataset, domainAxis, valueAxis, renderer);

JFreeChart chart = new JFreeChart("title", plot);

JPanel jPanel = new ChartPanel(chart);

Na początku tworzymy zbiór danych typu DefaultXYZDataset. Oznacza to, że nasze dane składają się z 3ch współczynników, co też widać w liniach 2 i 3. Następnie dodajemy nasze serie danych do zbioru. W kolejnych linijkach definiujemy nasze osie - jakie będą miały nazwy i zakres.

W linii 13 tworzymy obiekt typu PaintScale. Klasa ta jest odpowiedzialna za odpowiednie pokolorowanie „kwadracików” na wykresie. W przykładzie korzystam z przykładowej implementacji GrayPaintScale, która tworzy skalę szarości. Jak malować na kolorowo zostawiam jako zadanie domowe :P

Następnie definiujemy Renderer‘a a dokładniej XYBlockRenderer. Obiekt ten jest odpowiedzialny za odpowiednie wymalowanie danych na wykresie. Chcąc skorzystać ze wcześniej utworzonej skali, musimy ją przekazać za pomocą metody setPaintScale().

Następnie musimy spiąć razem zbiór danych, osie wykresu i obiekt renderujący w coś co się nazywa Plot, a w naszym przypadku XYPlot. Jest to klasa do której JFreeChart deleguje malowanie osi i danych. Mając już wspomniany XYPlot, możemy w końcu utworzyć nasz wykres (linia 19) i odpowiadający mu Panel (linia 21).

Poniżej efekt jaki możemy osiągnąć, wykonując poniższy kod:



Umiemy już namalować szare kwadraciki na wykresie, a stąd już niedaleko do żądanego efektu. Jak ktoś chce zobaczyć końcowy efekt to zapraszam do repozytorium MFCCChart na bitbucket.org

W następnym wpisie opiszę jeszcze jak namalować skalę kolorów.

niedziela, 20 listopada 2011

Problem sponsoringu konferencji


Wielu znajomych (i tych mniej znajomych) pyta mnie czasem o to, czy skoro tak licznie jeżdżę po konferencjach Javowych (w tym roku już 4 a to jeszcze nie koniec), to czy są to wyjazdy sponsorowane przez kogoś (pracodawcę / babcię), czy po prostu sam sobie je opłacam?

Otóż różnie to z tym bywa: czasem jadę z ramienia pracodawcy, a czasem za swoje (nie za mszowe) pieniądze. Uzasadniając takie postępowanie, podeprę się słowami Wujka Boba zapisanymi w książce The Clean Coder [str. 16].

“Your career is your responsibility. […]It is not your employer’s responsibility to train you, or to send you to conferences, or to buy you books. These things are your responsibility. Woe to the software developer who entrusts his career to his employer.”

Nie jest zadaniem / odpowiedzialnością pracodawcy płacić za Twój rozwój! Pracodawca daje Ci pracę, kupuje twoją dostępność, twoją wiedzę i umiejętności, dając Ci w zamian co miesiąc wypłatę przelewem na konto. Pracodawca „kradnie” z Twojego życia 40 godzin tygodniowo, oczekując w zamian rezultatów: skończonego, działającego projektu, udanego wdrożenia, zadowolenia klienta…

Pracodawca chce, aby u niego pracowali tylko najlepsi, ale najlepiej nie ponosząc przy tym żadnych kosztów związanych ze szkoleniami, wyjazdami na konferencje, czy z chociażby kupnem książki. Po prostu rozwój pracowników nie jest głównym celem / zadaniem pracodawcy. Od tego są uczelnie wyższe i instytucje prowadzące szkolenia. Te pierwsze mogą być bezpłatne (zależnie od państwa), a za szkolenia trzeba już sobie płacić.

Rozumiejąc taką politykę firm, powinniśmy tym bardziej doceniać swojego pracodawcę, jeśli ten wspiera nasz rozwój. Nie możemy jednak zapominać, że to My bierzemy odpowiedzialność za nasz rozwój i że to My musimy znajdować czas w swoim życiu, aby przeznaczyć go na swój rozwój.

A ile czasu powinniśmy poświęcać na samodoskonalenie?

Wujek Bob w przytaczanym fragmencie jego książki pisze o takim podziale tygodnia:

  • 40 h pracy dla pracodawcy (według ustawy)
  • 20 h pracy dla siebie (samodoskonalenie)
  • 56 h na sen (8h dziennie) 

No i jeszcze zostaje 52 h na wszystko inne: rodzina, przyjaciele, znajomi, aktywność fizyczna, odpoczynek. Myślę, że jest to bardzo optymistyczne oszacowanie. Wiadomo, nie jesteśmy robotami i możemy czasem mieć gorszy dzień lub tydzień, kiedy nic się nie chce. Ja te „zalecane” 20 godzin traktuję, jako oszacowanie, ile mniej więcej powinienem w tygodniu robić coś pożytecznego dla siebie. Czy osiągam te 20 godzin – nie wiem, nie mierzyłem tego.

A co robić w tym czasie?

No cóż, możliwości jest sporo i można o tym napisać osobny artykuł. Uczestnictwo w konferencjach / warsztatach branżowych jak najbardziej się zalicza do tych aktywności. Konferencje są różne i różne są ich poziomy, ale zawsze można się czegoś nauczyć. Nawet jak idziemy na prezentację, która nie do końca jest związana z naszym obszarem zainteresowań, możemy się dowiedzieć, że w ogóle coś takiego jest, jak działa i jak używać. A gdy mamy poznać nową technologię, to lepiej posłuchać kogoś kto miał z tym styczność, niż samemu rozkminiać tutoriale. Sam siłę i potęgę wykładów (chociażby studenckich) doceniłem dopiero na 4tym roku studiów, jak w końcu były one w miarę konkretne i gdy ktoś sensowny je prowadził.

Inną zaletą, jakie dają nam konferencje to możliwość pogadania i poznania ludzi z tymi samymi zainteresowaniami, co za sobą niesie wymianę doświadczeń, opinii itp. Inną bardzo miłą kwestią są wszelkie towarzyszące konkursy z nagrodami. Jak się już nie raz przekonałem, w cale nie jest tak ciężko coś wygrać (myszka Logitech’a na 4developers 2010, zniżki na zakupy w Helionie na Javarsovi 2010, pendrive'y i bilety do kina na Confiturze 2011). Ostatni konkurs po Warsjawie 2011 był bardzo fajny. Do rozlosowania były 2 książki ufundowane przez firmę TouK dla osób, które napiszą relację na blogu z konferencji. No i jedna z książek (Czysty Kod) przypadła mi, co pokazuje, że prowadzenie bloga może być przydatne ;)

Tak więc jak by ktoś jeszcze w tym roku chciał porobić coś ciekawego, to 3ciego grudnia jest Global Day of Coderetreat, czyli w ponad 80 miastach na świecie odbywać się będą warsztaty CodeRetreat. W Polsce taka impreza będzie w Krakowie na którą oczywiście się wybieram.

Szykuje się jeszcze Agile Development Day w Warszawie na który jeszcze można się próbować zapisać, a także w tym samym czasie Cracow.Mobi w Krakowie. A dla mieszkańców Wrocławia powstaje właśnie: Wrocław Google Technology User Group. Pierwsze spotkanie na temat Chrome Apps odbędzie się 29 listopada 2011 o godz. 19:00.

Prawdziwa gradka szykuje się jednak w pierwszym kwartale przyszłego roku, a mianowicie 33 degree. Ja już się zarejestrowałem, mimo że nie wiem czy sam będę musiał sobie ją opłacić, czy pracodawca mi ją zasponsoruje. W każdym bądź razie proszę Was, drogi czytelniku / czytelniczko, abyście podczas rejestracji podali mój adres email mstachniuk gmail com, dzięki czemu Wy nic nie tracicie, a ja zyskam zniżkę na tę konferencję. Szczegóły promocji: 33rd Degree Buzz - get 20% off.

Do zobaczenia na konferencjach!

czwartek, 27 października 2011

Wtyczki do Eclipse’a wspomagające naukę TDD


Aby zacząć naukę TDD (Test-driven development) wiele nie trzeba. Wystarczy ulubione środowisko programistyczne IDE (Idea / NetBeans / Eclipse) i narzędzie do testowania (JUnit / TestNG / xUnit). I teraz albo czytamy mądre książki (polecam "TDD by Example" Kenta Becka) jednocześnie kodując, lub szukamy odpowiednich informacji w necie, albo szukamy kogoś doświadczonego, aby pokodzić w parach. No i można już uskuteczniać TDD. Tylko skąd wiadomo, że robimy to dobrze? Czy pamiętamy o wszystkich krokach? Może czasem zapominamy zrefaktoryzować kod? Czy rzeczywiście mamy rytm red, green, refactor?

W celu śledzenia i późniejszej oceny jak nam poszło TDD warto zainstalować w Eclipse’ie plugin Pulse. Kiedy pierwszy raz o nim usłyszałem (i tu podziękowania dla Bartka), pomyślałem sobie: po co komu plugin do TDD? Pewnie to jakaś ściema lub zbędny bajer. I rzeczywiście jest to bajer, ale całkiem użyteczny.

Plugin Pulse tworzy wykres naszej aktywności, zaznaczając kiedy nasze testy fail’owały, kiedy były zielone i kiedy robiliśmy refaktoring. Na stronie plugin’a można zobaczyć ciekawy filmik prezentacyjny możliwości.

W celu przetestowania plugin’u wziąłem się wiec za PrimeFactorsKata, gdyż tej Katy wcześniej nie robiłem. Efekt poniżej:



Początkowo napisałem testy dla jedynki i dwójki (pierwsze dwie czerwone kropki). Gdy napisałem test dla 4ki to się skapnąłem, że skoro jest to rozkład na czynniki pierwsze, to powinienem zwracać listę wartości, a nie pojedynczą wartość. Trzeba było wiec sygnaturę metody zmienić.

Następnie szły kolejne testy, czasem w miarę potrzeby jakiś refaktoring. Jako refaktoring jest rozpoznawane użycie któregoś z automatycznych mechanizmów, np. Extract Method (Alt + Shift + M), czy Rename (Alt + Shift + R). Niestety, jak robimy to ręcznie, to nie jest to wykrywane przez plugin. Ale w sumie niedziwne, gdyż wówczas każda edycja kodu by generowała niebieskie punkty na wykresie.

Gdy już nie chciało mi się już szukać (ani liczyć) kolejnych testowych wartości, które bym dodał do testów, przerwałem katę. Nie napisałem w pełni funkcjonalnej metody, gdyż pewnie dla jakiś kolejnych dużych wartości mogło by to nie działać. Ale nie o to chodzi w ćwiczeniach typu CodeKata by dojść do końca (bez skojarzeń;) ), a o poznanie jakiejś nowej techniki programistycznej, lub wyrobieniu sobie dobrych nawyków.

Wracając jeszcze z Warsjawy, co opisywałem we wpisie: Warsjawa 2011 już za nami, jechałem pociągiem i wziąłem się za BowlingGameKata. Tutaj już miałem sporo czasu na implementację. Nie było to moje pierwsze podejście do tego problemu, więc już jakoś w podświadomości było wiadomo, jak to rozwiązać. Ćwiczyłem dodatkowo tworzenie notatek, na temat tego, jakie testy trzeba jeszcze napisać i jakie są już napisane. Wykres z wtyczki Pulse poniżej:



Zacząłem równo o 16.00 i początkowo pisałem proste testy dla następujących przypadków (liczby to ilości kręgli zbitych w danym rzucie):
  • 0 - pierwszy zielony test.
  • 1- pierwszy czerwony i już po nim jakiś refaktoring.
  • 1, 3 - drugi czerwony test.
  • 1, 3, 4, 5 - któryś z zielonych testów po 16.10.
  • Pierwszy bonus: Spare – zacząłem o 16.14 i wtedy postanowiłem zrobić większy refaktoring, tj. wprowadzić design obiektowy. Skończyłem go o 16.30 i 5 minut później już mi śmigał pierwszy test dla Spare’a. Do 16.40 jeszcze coś refaktoryzowałem, co nawed plugin zarejestrował.
  • Double Spare  - zacząłem chwile po 16.40 i trochę musiałem podebugować do 16.55. Ostatecznie okazało się, że w teście źle przeliczyłem oczekiwaną wartość. 
  • Strike – o 17.03 skończyłem implementację drugiego bonusu.
  • Double Strike – skończyłem implementację o 17.33. W międzyczasie musiałem zmienić przedział w pociągu, gdyż skończył się prąd w gniazdku :P
  • Spare, Strike – skończyłem o 17.39 (miałem błąd w danych testowych)
  • Strike, Spare– skończyłem o 17.41(j.w.). Następnie chciałem napisać implementację Full Strike (czyli ciągle rzucamy 10 za pierwszym razem). Ale po tym jak test zfail’ował, dopisałem do mojej listy TODO jeszcze inne, prostsze testy dotyczące końcówki gry (ostatnia ramka) i zacząłem od nich implementację.
  • Spare w ostatniej ramce – skończyłem o 17.54.
  • Strike w ostatniej ramce – skończyłem o 18.00.
  • Double Strike w przedostatniej (i ostatniej) ramce skończyłem o 18.03.
  • Full Strike (ciągle rzucamy 10) – działał od razu po odkomentowaniu testu, gdyż wcześniejszymi testami zapewniłem odpowiednie działanie ostatniej ramki. Na koniec jeszcze usunąłem duplikacje z kodu ekstrachując odpowiednie metody.

Pierwszy raz doprowadziłem tą Katę do końca i jestem z niej zadowolony. Teraz moją implementację trzeba porównać z rozwiązaniem wujka Boba.

Kolejnym pluginem udostępnianym przez @iamhappyprog na stronie www.happyprog.com jest TDGotchi. Dzięki kolejnym wynikom testów zbieramy punkty i rozwiajamy nasze „zwierzątko”.

Podczas PrimeFactorsKata udało mi się uzbierać 5 punktów, przy bowlingu zapomniałem spojrzeć.



Przy zamknięciu i otwarciu widoku, punkty się resetują. możemy jeszcze obserwować nasze zwierzątko / ikonke jak się zmienia, gdy zminimalizujemy widok.

Ostatnim plugin’em tego samego autora jest PairHero. Wspiera on Pomodoro Technique i ping pong programing.  Za przechodzące testy i refaktoring dostajemy punkty, a za szybkie zmiany kierownik – pilot można dodatkowo pomnożyć te punkty. Fajne jeśli akurat ćwiczmy Ping Pong'a, szybkie zmiany, lub po prostu programowanie w parach.Przykładowy screenshot poniżej.



Z przedstawionych plugin’ów najbardziej polecam pierwszy, gdyż wg. Mnie niesie największą wartość i pozwala na dobrą retrospekcje jak i porównanie wykresu z innymi developerami. Pozwala również nam zauważyć, jak często wykonujemy automatyczny refaktoring, ile czasu potrzebujemy na przejście red -> green itp.

A jak wygląda Twój puls?

niedziela, 23 października 2011

Mobilization – konferencja o technologiach mobilnych - wrażenia


Ledwie tydzień temu byłem na Warsjawie, a tu już następna konferencja: Mobilization.pl, która odbyła się w sobotę w Łodzi. Była to pierwsza edycja konferencji i jeszcze sporo pozostawia do życzenia co do poziomu wykładów, ale miejmy nadzieję, że w przyszłości się to poprawi.

O 9.50 było szybkie powitanie uczestników, parę spraw organizacyjnych i rozpoczął się pierwszy wykład Bartłomieja Zassa na temat programowania Windows Phone Mango. Mimo, że nie jestem fanem rozwiązań tej firmy, byłem pod wielki wrażeniem tej prelekcji jak i prelegenta. Przede wszystkim prezentacja była bardzo dobrze przygotowana, było kodowanie na żywo (które działało), opcja zoom na ekranie, zaznaczanie kursorem elementów na ekranie, pokaz działania telefonu na rzutniku, płynne przełączanie się pomiędzy kodem a prezentacją i jeszcze parę innych fajnych drobnostek. Istny majstersztyk. Jedyne co mi się nie spodobało, to częściowe ucięcie prawej strony ekranu prezentacji (złe wyskalowanie ekranu – można było wcześniej ustawić), i mówienie „na pan/państwo”. Ja wolę luźniejszy sposób prowadzenia prezentacji i zwracanie się „na Ty/Wy”.

Na samej prezentacji najpierw było trochę historii. W 2010 była premiera Windows Phone, ale nie w Polsce (gdyż nie było tłumaczenia na nasz ojczysty język) i oficjalnie nie można było kupić u nas takiego telefonu. Jednak jesienią 2011 pojawił się Windows Phone 7.5 Mango dostępny dla Polaków. Były na prezentacji podane minimalne wymagania sprzętowe, które musi posiadać telefon, aby obsługiwać ten system. Wymagania są dość spore co może wpływać na cenę urządzeń z produktem Microsoftu. Na dniach Nokia ma wypuścić Nokię 800 która ma działać na Windows Phone 7.

Na przedstawiany system można tworzyć oprogramowanie z wykorzystaniem Silverlight 4 czy też XNA (api dla XBoxa). Mango zawiera również Internet Explorer’a 9 (niby ten sam engine co w PC’tach), czyli daje to nam wsparcie dla Html’a 5 i sprzętowej akceleracji graficznej. Bartłomiej pokazywał słynny test FishIETank  odpalany na telefonie i wyglądał dobrze.

Telefony z Windowsem Phone 7 prezentują trochę inne podejście od obecnie popularnych smartfonów. Zamiast w menu wyświetlać ikonki, które uruchamiają poszczególne aplikacje, wprowadzono koncepcję kafelków. Są to takie większe ikonki, które pokazują jakiś stan aplikacji, np. informację, czy przyszły nowe e-maile. I nasze aplikacje (o ile to obsługują) można przypiąć do pulpitu jako taki kafelek. Ciekawe podejście, choć gusta są podzielone na temat tego rozwiązania.

Inną wprowadzoną do systemu koncepcją są Hub’y, czyli integracja informacji z wielu miejsc. Przykładowo w książce telefonicznej pod konkretnym kontaktem możemy mieć zagregowane informacje o danej osobie z facebook’a, linkedin’a gmail’a i innych. Fajna funkcjonalność.

Następnie było na prezentacji już bardziej praktycznie. Prelegent pokazał kilka przykładowych aplikacji i jak można prosto i szybko tworzyć własne. Bartek stworzył na szybko aplikację o konferencji dla Windows Phone 7.5. Pokazał przy tym jak można to robić z poziomu Visual Studio jak i Expression Blend 4. Oba narzędzia dobrze się integrują i synchronizują. Pierwsze służy programistom, a drugie jest bardziej dla grafików i projektantów interfejsów graficznych, ale można w nim również sporo funkcjonalności wyklikać. Jeśli chodzi o licencjonowanie to narzędzia są bezpłatne.

Microsoft udostępnił również (podobnie jak konkurencja) miejsce gdzie można umieszczać i sprzedawać swoje aplikacje, czyli Marketplace. Studenci mogą sobie za darmo konto założyć, osoby prywatne i firmy płacą 100 $ rocznie.

Prezentację uważam za najbardziej udaną i pokazującą najwięcej ze wszystkich prezentacji w jakich uczestniczyłem w ramach tej konferencji. Wystąpienie się przedłużyło, przez co cała agenda się przesunęła o 15 minut.

Kolejną prezentacją na której byłem, było Programowanie Android 3.0 prowadzone przez Krzysztofa Janika. Prezentacja słaba, już na drugim slajdzie były jakieś błędy. Prelegent chciał zrobić małe demo pokazywane z tableta (bo tam działa Android 3.0), ale miał jakieś problemy z kablem. Próbował jeszcze włączyć live streaming (cała konferencja była stream’owana na giwebcast.pl) na rzutniku, by pokazać tablet do kamery. Niestety nie udało się załadować streamingu na komputerze prelegenata :( Dodatkowo Krzysiek często patrzył na slajdy i zadawał dużo pytań publiczności, które nie wnosiły nic do tematu. Bardzo słabe wystąpienie.

Jedyne co wyniosłem z prelekcji to obecność w Androidzie 3.0 czegoś co nazywamy Fragments. Są to jakby miniActivity, które możemy dynamicznie osadzać w innych Activity.

Kolejne wystąpienie było Konrada Hołowińskiego na temat Multiplatform mobile apps. Konrad przedstawił dwa rozwiązania pozwalające pisać raz i uruchamiać aplikację na różnych urządzeniach mobilnych.

Pierwszym omawianym rozwiązaniem było Appcelerator Titanium. Aplikację tworzymy za pomocą JavaScript’u wykorzystując dodatkowo natywne komponenty dla konkretnych platform. Titanium dorzuca później do kodu własny interpreter JS, co zwiększa wielkość aplikacji. Jeśli dobrze zrozumiałem, to od pewnego czasu produkt ten używa statycznej analizy i kompilacji kodu, generując kod natywny dla danej maszyny. Zdecydowano się na taki krok ze względów wydajnościowych, ale przez to pojawiały się przez jakiś czas wycieki pamięci.

Titanium wspiera Androida, iOSa i BlackBery (nie w pełni). Dzięki  kompilacji do natywnego kodu, aplikacje przyjmują wygląd platformy na której są uruchamiane. Pojawiają się przez to często różnice w wyglądzie na innych urządzeniach, co zostało pokazane podczas prezentacji na emulatorach iPhone’a i Androida. Mamy również dostępne darmowe jak i płatne plugin’y rozszerzające możliwości dostarczane przez Titanium.

Następnie było o konkurencyjnym rozwiązaniu PhoneGap, które wspiera więcej (bo aż 6) platform. Tutaj piszemy w HTMLu 5, JavaScript’cie i CSS3. Produkt jest typu open-source i wszelkie plugin’y są rozwijane przez społeczność.

PhoneGap jest uruchamiany w natywnym silniku JS danego urządzenia. Daje on jednak też dostęp do większości funkcjonalności dostępnych w smartfonach, za pomocą „zmostkowania” ich do JS. Podejście to ma swoje wady jak i zalety. Wadą jest brak wsparcia dla wielowątkowości i wolniejsze działanie od aplikacji tworzonych w Titanium, lub natywnie. Za to naszą aplikację możemy uruchomić w przeglądarce i podejrzeć cos się dzieje np. za pomocą FireBug’a.

Z ciekawostek, to PhoneGap zostało ostatnio przejęte przez firmę Adobe, które się stara zaistnieć na rynku urządzeń mobilnych. Zobaczymy co z tego wyjdzie.

Na konferencji miałem okazję poznać Tomka Dziurko. Po prezentacji Konrada poszliśmy na obiad i po nim chcieliśmy pójść na Advanced layouts on the Android battlefield. Jacka Niedzwieckiego, ale chwilkę się spóźniliśmy, a sala była wypchana po brzegi. Alternatywny wykład o Windows 7 nas nie interesował, więc se posiedzieliśmy w holu konferencji.

Następnie miał być wykład o Androidzie i SmartTV, ale prelegent nie przyjechał. W jego miejsce wrzucono Testowanie w Systemie Android Marka Defecińskiego. Marek był jednym z organizatorów konferencji i prowadzi Blog Progamisty Android. Marek jest niespełnionym fanem testów. Swoją opowieść zaczynał od J2ME, w której sam miałem przyjemność się sprawdzić. I rzeczywiście potwierdzam, że o testowaniu w J2ME nie ma co mówić.

Później już było o Androidzie i testowaniu funkcjonalnym za pomocą robotium. Projekt się reklamuje jako Selenium ale dla Androida i działa na emulatorze Androida. Prelegent nie był jednak do końca zadowolony z tego rozwiązania.

Innym ciekawym, omawianym projektem był Robolectric. Jest to framework do testów, który za pomocą Javassist podmienia to co siedzi w android.jar. Dzięki temu możemy nasze testy odpalać na komputerze, działa refaktoryzacja i jest to szybkie.

Było jeszcze wspomniane o android-mock i paru innych frameworkach do testowania. Niestety Marek nie pokazał żadnych przykładów, zrzucając winę na aktualizacje maven’a. Brakuje mu jeszcze oswojenia z publiką i rozluźnienia podczas prowadzenia prezentacji. Na problem suchego gardła polecam wodę z cytryną przed i w trakcie prezentacji (powoduje wydzielanie śliny) jak i unikania słodyczy oraz nadmiernego picia wody (wysusza gardło). Ponadto bujanie się na boki jest trochę denerwujące / rozpraszające dla słuchaczy. Niestety pewność na tego typu wystąpieniach można wyćwiczyć jedynie występując.

Następne prelekcje były dla mnie mniej interesujące. Spotkałem na konferencji Pawła Zubkiewicza, którego znam dzięki WrocJUG. Jako, że Paweł jest z Wrocławia, postanowiłem się zabrać z Nim i innymi pracownikami QNH do domu, gdyż powrót autobusem o 21.35 z Łodzi jakoś mi się nie uśmiechał.

W drodze powrotnej nie mogło się obejść bez niespodzianek, mianowicie klocki hamulcowe się starły i musieliśmy się zatrzymać. Na całe szczęście wylądowaliśmy w miejscowości Szadek, a nie gdzieś w lesie. Dobrą pizze tam mają i gdyby nie ta awaria nigdy bym się tam nie pojawił. Pomoc drogowa zawiozła nas z powrotem do Łodzi i tam już czekał na nas samochód zastępczy, którym dojechaliśmy do Wrocławia.

Podsumowując, jak na pierwszą edycję konferencji, organizacja wypadła dobrze. Były dostępne przekąski w postaci kanapek i jogurtów (świetny pomysł) jak i kawa / herbata. Jedna sala była trochę mała, przez co nie chciałem się dociskać na jeden wykład. Na krzesełkach konferencyjnych były fajne podkładki przyczepiane do krzesła rzemykiem, na których się wygodnie notowało. Trzeba jedynie poziom prezentacji podnieść i będzie cacy.