czwartek, 31 lipca 2008

Groovy w praktyce - part 5 - mapy 2/2

Pora wrócić do Groovy po drobnej przerwie.

Pewną szczególną cechą Groovy jest podejście do interfejsów w połączeniu z mapami i domknięciami. Tym razem włączam groovyConsole i...
interface Kaczka{
void kwa()
void lataj()
}

def PrawieJezioroLabedzie(Kaczka k){
6.times{k.kwa()}
}

def mapa = [ kwa:{println 'kwa'}, lataj: {println 'ja latam!'} ]
def kaczka = mapa as Kaczka

if(kaczka instanceof Kaczka){
PrawieJezioroLabedzie(kaczka)
kaczka.lataj()
}
Wynik:
kwa
kwa
kwa
kwa
kwa
kwa
ja latam!
To przecież jakaś herezja ! Dynamicznie tworzę instancję interfejsu za pomocą zawartości mapy. Jak to możliwe? W Javie od wersji 1.3 można dynamicznie tworzyć nowe implementacje klas i interfejsów! Jest to możliwe za pomocą Dynamic Proxy, łatwo można sprawdzić, że Groovy wykorzystuje ten mechanizm.
println java.lang.reflect.Proxy.isProxyClass(kaczka.class)
Wynik:
true

Wstrzykiwanie zależności, aspekty, zarządzane ziarna, binding, rmi ... wszystko to wykorzystuje dynamic proxy. Ma to swoje minusy. Proxy, jak przystało na twór dynamiczny, będzie wolniejszy od normalnej klasy. Jest to cena warta zapłacenia, jeśli chcesz stworzyć jakikolwiek zarządzany komponent to trudno nie wykorzystać proxy. .Net ma podobny twór o mniejszych możliwościach (bo m.in. w C# metody są domyślnie nie wirtualne, takiego wywołania nie można dynamicznie przechwycić, więc tylko niektóre metody proxy może implementować).

Można też rzutować pojedyncze domknięcie na interfejs
PrawieJezioroLabedzie({println "ćwir"} as Kaczka)
Wynik:
ćwir
ćwir
ćwir
ćwir
ćwir
ćwir

Wtedy wszystkim funkcją interfejsu zostanie przypisana jedna akcja. Co ciekawe niektóre z propozycji domknięć dla Java pozwalają na niejawne zamienianie domknięć na interfejsy. Jest to niebywale wygodne, gdy definiujemy zdarzenia np. w GUI.

Groovy sprawdza w trakcie wykonania typy obiektów dlatego poniższy kod jest nie poprawny.
PrawieJezioroLabedzie(mapa)
// powoduje
Exception thrown: groovy.lang.MissingMethodException: No signature of method: Script12.PrawieJezioroLabedzie() is applicable for argument types: (java.util.LinkedHashMap)

Jeśli jednak usunę informację o oczekiwanym typie z parametru funkcji, to funkcja nie będzie widzieć różnicy między klasą, a mapą i w obu przypadkach wykona się poprawnie.
def PrawieJezioroLabedzie(k){
6.times{k.kwa()}
}

PrawieJezioroLabedzie(mapa)
Wynik
kwa
kwa
kwa
kwa
kwa
kwa

Więcej w implementacja interfejsów w Groovy

środa, 30 lipca 2008

Groovy w praktyce - part 4 - mapy 1/2

Mapa w Groovy jest reprezentowana przez zwykłą klasę Javy, używać ją można jak zwykłą mapę, klasę asocjacyjną, a nawet obiekt.
groovy> a = [:]
===> {}

groovy> a.getClass()
===> class java.util.LinkedHashMap

// iteracje po mapie mozna wykonac na kilka sposobow
groovy> a = [a:'aa',b:'bb']
===> {a=aa, b=bb}

groovy> a.each{println it}
a=aa
b=bb
===> {a=aa, b=bb}

groovy> a.each{println it.key}
a
b
===> {a=aa, b=bb}

groovy> a.each{println it.value}
aa
bb
===> {a=aa, b=bb}

groovy> a.each{ key,value -> println key + ' - ' + value }
a - aa
b - bb
===> {a=aa, b=bb}

// ustawianie i odczytywanie elementów jest proste,
groovy> a.a
===> aa
groovy> a.b
===> bb
groovy> a.c
===> null
// ale z powodu takiej konwencji a.class == null, dlatego wcześniej użyłem getClass()


// na mapie można bezpośrednio tworzyć nowy element w analogiczny sposób
groovy> a.c = 'cc'
===> cc
groovy> a
===> {a=aa, b=bb, c=cc}
groovy> a['d'] = 123
===> 123
groovy> a
===> {a=aa, b=bb, c=cc, d=123}

groovy> a.keySet()
===> [a, b, c, d]
groovy> a.values()
===> [aa, bb, cc, 123]

Klucz w mapie domyslnie jest traktowany jako string, chyba ze jest liczbą, wartością logiczną lub null.
groovy> b = [1:1,a:2]
===> {1=1, a=2}
groovy> b[1]
===> 1
groovy> b['a']
===> 2
groovy> b.'1'
===> null
groovy> b[a]
===> null

Jeśli chcemy jako klucz użyć coś innego niż liczbę czy string to możemy użyć notacji tabeli asocjacyjnej mapa[obiekt], albo nawiasów():
groovy> x = [1,2]
===> [1, 2]
groovy> y = [ (x):'abc' ] //dzieki nawiasom w tym przypadku kluczem będzie lista
===> {[1, 2]=abc}
groovy> y.keySet()*.getClass()
===> [class java.util.ArrayList]
groovy> y.each{println it.key[1]}
2
===> {[1, 2]=abc}

Jeśli mapa jest parametrem funkcji to większości przypadków można porzucić nawiasy []
groovy> f = {mapa -> mapa.values().each{ println it }}
===> groovysh_evaluate$_run_closure1@1e46a68
groovy> f([a:1])
1
===> [1]
groovy> f([a:1,b:2])
1
2
===> [1, 2]
groovy> f(a:1,b:2)
1
2
===> [1, 2]
nawet jeśli mapa nie jest jedynym parametrem
groovy> g = {mapa, foo -> println mapa}
===> groovysh_evaluate$_run_closure1@d480ea
groovy> g(a:1,2)
["a":1]
===> null
groovy> g(a:1,b:2,2)
["a":1, "b":2]
===> null

Języki dynamicznie z domknięciami mają ciekawą cechę, mapa odpowiada w pewnym stopniu obiektowi, np. w ActionScript 2 nie ma osobnej kolekcji "Mapa" - korzysta się z Obiektów i dynamicznie tworzy nowe pola wygląda to tak jak kod w 3 linijce.
groovy> a = [:]
===> {}
groovy> a.pole = 1
===> 1
groovy> a.funkcja = { x -> x * a.pole } // ! trzeba zapisać skąd pochodzi "pole", m.in. tym się różni od normalnego obiektu
===> groovysh_evaluate$_run_closure1@20f237
groovy> a.funkcja(2)
===> 2
groovy> a.pole = 2
===> 2
groovy> a.funkcja(2)
===> 4
groovy>

Teraz, chwila przerwy, by każdy czytelnik mógł się zastanowić jak proste jest tworzenie mocków w Groovy za pomocą takich opcji.




Istnieje jeszcze jedna specjalna klasa - Expando - która w pewien, jak na razie magiczny, sposób pozwala na bardziej naturalne tworzenie dynamicznych obiektów.
groovy> e = new Expando()
===> {}
groovy> e.pole = 1
===> 1
groovy> e.f = { x -> x * pole } // ! nie trzeba specyfikować z jakiego obiektu pochodzi pole, czyli to czego oczekujemy
===> groovysh_evaluate$_run_closure1@174aa60
groovy> e.f(2)
===> 2
groovy> e.pole = 2
===> 2
groovy> e.f(2)
===> 4

Przetestuj poniższy kod, zapamiętaj co się dzieje [ zakładam, że wiesz dlaczego ;) ]
a = [a:1]
e = new Expando()
e.a = 1
e.f = { x -> x * a }
e.f(2)

Więcej o mapach

wtorek, 29 lipca 2008

Groovy w praktyce - part 3 - listy i kilka operatorów

Najpierw parę operatorów.
  • *. - pozwala na zgrupowanie wyników pewnej operacji a*.b() odpowiada a.collect{ it.b()}, domyślny sposób nawigacji w OCL i XPath
  • ?. - bezpieczna nawigacja - jeśli a= null to a?.b też wynosi null
  • ?: - doskonały dla wartości domyślny - zwraca wartość po prawej jeśli po lewej jest null, pusty string, false lub 0
//
// Operator .*
//
groovy> "abc".toList()
===> [a, b, c]
groovy> a = "abc".toList()
===> [a, b, c]
groovy> a*.toUpperCase()
===> [A, B, C]
groovy> a.collect{ it.toUpperCase()}
===> [A, B, C]

//
// Operator ?.
//
groovy> class A{ def a }
===> true

groovy> a = new A()
===> A@c1a0eb
groovy> a.a = new A()
===> A@ed9f47
groovy> a.a.a = new A()
===> A@1762027

groovy> a.a?.a
===> A@1762027

groovy> a.a = null
===> null
groovy> a.a?.a
===> null
groovy> a.a.a
ERROR java.lang.NullPointerException: null
at groovysh_evaluate.run (groovysh_evaluate:1)
...

//
// Operator ?:
//
groovy> null?:2
===> 2

groovy> 1?:2
===> 1
groovy> 0?:2
===> 2

groovy> true?:2
===> true
groovy> false?:2
===> 2

groovy> ''?:2
===> 2
groovy> 'x'?:2
===> x


A teraz listy, niech kod mówi sam za siebie.


Groovy Shell (, JVM: 10.0-b22)
Type 'help' or '\h' for help.
-------------------------------------------------------------------------------

groovy> a = [1,2,3]
===> [1, 2, 3]
groovy> a.class
===> class java.util.ArrayList

groovy> a + 4
===> [1, 2, 3, 4]
groovy> a
===> [1, 2, 3]

groovy> a << 4
===> [1, 2, 3, 4]
groovy> a
===> [1, 2, 3, 4]

groovy> a - 3
===> [1, 2, 4]
groovy> a
===> [1, 2, 3, 4]

groovy> a -= 3
===> [1, 2, 4]
groovy> a += 3
===> [1, 2, 4, 3]
groovy> a
===> [1, 2, 4, 3]

groovy> a.sort()
===> [1, 2, 3, 4]

groovy> a + [5,6,7]
===> [1, 2, 3, 4, 5, 6, 7]
groovy> a
===> [1, 2, 3, 4]


groovy> a << [5,6,7]
===> [1, 2, 3, 4, [5, 6, 7]]


groovy> a.pop()
===> [5, 6, 7]

groovy> a
===> [1, 2, 3, 4]

groovy> a += [5,6,7]
===> [1, 2, 3, 4, 5, 6, 7]

groovy> a += (8..10)
===> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

groovy> a[0]
===> 1
groovy> a.get(0)
===> 1
groovy> a.getAt(0)
===> 1
groovy> a.getAt([0,-3..-1])
===> [1, 8, 9, 10]
groovy> a[-1]
===> 10
groovy> a[4..-1]
===> [5, 6, 7, 8, 9, 10]
groovy> a == [*1..10]
===> true

groovy> a.each{println it}
1
2
3
4
5
6
7
8
9
10
===> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

groovy>a.reverseEach{ println it}
10
9
8
7
6
5
4
3
2
1
===> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
groovy> a.reverse().eachWithIndex{it,i -> println "$i: $it"}
0: 10
1: 9
2: 8
3: 7
4: 6
5: 5
6: 4
7: 3
8: 2
9: 1
===> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
groovy> a
===> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

groovy> a.find{it > 3}
===> 4
groovy> a.findAll{it > 3}
===> [4, 5, 6, 7, 8, 9, 10]

groovy> a.every{it > 3}
===> false
groovy> a.any{it > 3}
===> true

groovy> a.max()
===> 10
groovy> a.min()
===> 1
groovy> a.sum()
===> 55

groovy> a.join()
===> 12345678910
groovy> a.join('-')
===> 1-2-3-4-5-6-7-8-9-10

groovy> a.intersect([8,9,10,11,12,13])
===> [8, 9, 10]
groovy> a.contains(1)
===> true
groovy> a.containsAll([1,3,5])
===> true
groovy> a.disjoint([10,11])
===> false
groovy> a.disjoint([11,12])
===> true

groovy> a.multiply(2)
===> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
groovy> a
===> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
groovy> a * 2
===> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

groovy> a = "abc".toList()
===> [a, b, c]


Więcej: Groovy - Kolekcje

niedziela, 27 lipca 2008

Groovy w praktyce - part 2 - "zapomnij o pętlach" czyli elementy stylu funkcyjnego

Dziś opowieść o funkcjach. Przyjmuje na razie pewne uproszczenie, bo tak naprawdę to nie funkcje ale domknięcia (closure) używam. Jednak wiedza czym domknięcia różnią się od funkcji nie jest na razie potrzebna i można.

Plan jest prosty
  1. Jak używa się pętli w Groovy?
  2. Jak to jest możliwe, że to da się tak zrobić?
Odpalam groovysh. Linijki zaczynające się od groovy> to kod wpisany do interpretera. Tekst zaraz za taką linijką, a przed ===> to wyniki println, wartość po ===> to wartość wyrażenia.

Pewną archaiczną konstrukcją języka odziedziczoną po Javie są pętle ;)
int i = -1;
while(i++ < 3){
System.out.println(i);
}
// lub
for(int i = 0; i < 4; i++){
System.out.println(i);
}

Groovy rzeczywiście akceptuje tak napisany kod.
groovy> for(int i = 0; i < 4; i++){ System.out.println(i); }
0
1
2
3
===> null

Ja jednak preferuje styl funkcyjny.
groovy> 4.times{ it -> println(it) }                      
0
1
2
3
===> null

// A najbardziej lubie

groovy> (0..3).each{ println it }
0
1
2
3
===> null

Mając więc do wyboru
for(int i = 0; i < 4; i++){
System.out.println(i);
}
lub
(0..3).each{ println it }

Co wybierzecie? Gdzie łatwiej stwierdzić ile razy coś się wykona, albo co właściwie się stanie?
Nie przekonani? To może inny przykład
String s = "abcde";
for(int i = 0; i < s.length(); i++){
System.out.println(i);
}

//względem

"abcde".each{ znak -> println znak }

No ale jak to jest właściwie możliwe ?
Wszystkie kolekcje oraz String i zakresy w Groovy mają przydatne funkcje jak each, find, collect, every, any itd. o których więcej w następnych częściach. Na razie wystarczy wiedzieć, że funkcje te pobierają jako parametr zmienną będącą funkcją. Funkcja ta pobiera jako parametr jeden obiekt z kolekcji.

No dobrze, ale żeby przesłać funkcję do innej funkcji najpierw trzeba ją zdefiniować.

W Java można tworzyć funkcje jedynie bezpośrednio w klasach, Groovy tak też potrafi.
// Java
class A{
public void f(Object x){
System.out.println(x);
}
}

// Groovy
groovy> class A{ public void f(Object x){ System.out.println(x);} }
===> true

// W Groovy można też pominąć wszystkie typy, pozbyć się średnika i skrócić System.out... do samego println
class B{
public f(x){
println(x)
}
}

//uruchamiam funkcję
groovy> a = new A()
===> A@1de498
groovy> a.f(1)
1
===> null

W Groovy można definiować funkcję praktycznie w dowolnym miejscu. Możemy to zrobić np. za pomocą def.
groovy> def f(x){ println(x) }
===> true
groovy> f(1)
1
===> null

// tak zdefiniowaną funkcje mogę już wysłać do innej funkcji np.:

groovy> (0..3).each(f)
0
1
2
3
===> 0..3

W językach funkcyjnych często tworzy się funkcje anonimowe, które są przypisywane zmiennym lub parametrom innych funkcji. Przykładowo możemy przypisać zmiennej g funkcję pobierającą Object i wypisującą go na ekran jako:
g = { Object it -> println(it); } // ten zapis możemy uprościć pozbywając się informacji o typie i średnika
g = { it -> println(it) } // nawiasy też nie są potrzebne ponieważ f(x) == f x - podobnie jak w Ocaml
g = { it -> println it }

// ostatecznie mamy:
groovy> g = { it -> println it }
===> groovysh_evaluate$_run_closure1@10e434d
groovy> g(1)
1
===> null

// tak stworzoną funkcję możemy ponownie wysłać do innej funkcji:
groovy> (0..3).each(g)
0
1
2
3
===> 0..3

(0..3).each(g) // możemy ponownie pozbyć się nawiasów
(0..3).each g // właściwie nie ma powodu by definiować osobną zmienną, możemy funkcję zdefiniować natychmiast
(0..3).each { i -> println i} // tutaj nazwaliśmy parametr i,
// co ciekawe parametr o nazwie it jest parametrem domyślnym i nie trzeba go w ogóle specyfikować
// więc można napisać:
(0..3).each { println it }

Ostatnia linijka kodu definiuje anonimową funkcję pobierająca jeden obiekt pod nazwą it i każe wykonać tą funkcję dla każdego obiektu z zakresu od 0 do 3 włącznie.
I tym sposobem zatoczyłem koło.

Na taki styl pozwalają języki, które traktują funkcje jak zwykłe obiekty (First-class function), więc np. JavaScript, Ruby, Python, ActionScript, Scala. Jeśli pozwalają to na pewno z niego korzystają. Jeśli komuś się taki styl nie podoba, to muszę przyznać, że ma pecha. Nawet następny standard C++ (C++0x) będzie najpewniej mieć domknięcia, C# już je ma, a Java ma 3 propozycje domknięć i prawdopodobnie w Java 1.7 domknięcia już będą. Pewnie nie tak ładnie syntaktycznie jak w Groovy, ale będą.
Innymi słowy w 2010 wszystkie główne języki programowania będą obsługiwać domknięcia. I to pełne domknięcia, nie tylko funkcje jako obiekty (a co to tak naprawdę oznacza, to może innym razem).

piątek, 25 lipca 2008

Groovy w praktyce - part 1 - instalacja i String

Wymagania wstępne
Java, najlepiej >=1.5. Na 1.4 będzie działać, ale bez niektórych opcji.

Instalacja - Linux

Ewentualnie wszelkie pliki można dostać tu http://groovy.codehaus.org/Download w tym uniwersalny instalator i paczki dla innych dystrybucji.

Instalacja - Windows
Instalator Groovy 1.5.6 + Gant 1.3

Test
Po przeprowadzeniu instalacji pora przetestować GroovyConsole. W Windows powinna pojawić się pozycja w menu star. W obu systemach w linii komend powinno wystarczyć wpisanie groovyConsole, by włączyła się poniższa aplikacja. Wpisujemy w górną część print "Hello world" i klikamy zaznaczony przycisk:
GroovyConsole jest najprostszym edytorem Groovy, cały kod w edytorze / pliku jest wykonany, a rezultat zwracany.
W Windows kliknięcie na plik z rozszerzeniem .groovy powoduje ich wykonanie. Jak widać Groovy nie wymaga by kod był pisany w metodach.

Linia komend - groovyc
Kompilator, na razie nie będzie potrzebny.

Linia komend - groovy
Interpreter.
  • groovy Hello.groovy - wykonuje kod z podanego pliku
  • groovy -e " print 'Hello world' " - wykonuje bezpośrednio podany kod
Linia komend - groovysh
Interaktywny interpreter.
Interaktywny interpreter to ciekawa pomoc w nauce dostępna w językach takich jak Groovy, Ruby czy Python. A działa to tak:

kk@kkl:~$ groovysh
Groovy Shell (, JVM: 10.0-b22)
Type 'help' or '\h' for help.
-------------------------------------------------------------------------------
groovy:> a = "123" [enter]
===> 123
groovy:> b = 4
===> 4
groovy:> c = 5.0
===> 5.0
groovy:> a+b+c
===> 12345.0
groovy:> "$c - ${b} = ${c-b}"
===> 5.0 - 4 = 1.0 //to jest String !
W interpreterze możemy na bierząco tworzyć zmienne (nawet bez ich deklaracji) i przypisywać im dowolne wartości. Po każdej komendzie lub zestawie komend interpreter wykonuje kod i zwraca wynik. Wynikiem jest zawsze wartość ostaniej komendy.
Dla komendy 1 będzie to 1, dla komendy a = 1 będzie to 1, dla komendy 1;2;3 będzie to 3 a dla komendy print 1 będzie to null (po drodze zostanie wypisane 1).

Linijka13 - GString
Jeżeli Groovy spotka w String napis postaci $nazwa_zmiennej to wstawi w jej miejsce wartość zmiennej. Jeżeli spotka ${kod} to wstawi w to miejsce wykonany kod.
Dalej Stringi można zapisywać w formie:
  • "string" lub 'string' - zwykły String
  • /C:\Windows\system / - String w którym nie trzeba używać \
  • Stringi na kilka linijek:
def s = """\
String długi na
${a[1]} linijki
"""
Ten ma dokładnie 2 znaki nowej linii.

Kod w Stringach zauważa zmiany zmiennych:
groovy:> "abcdef".each{print "  $it"}
a b c d e f===> abcdef
Zmienna it - domyślna zmienna dla funkcji iterujących - przyjmuje wartość kolejnych znaków i za każdym razem nowa wartość jest wstawiana do stringa.
groovy:> "abcde"[2..4]
===> cde
groovy:> "abcde"[2..-2]
===> cd
groovy:> "abcde"[3..0]
===> dcba

groovy:> "abcde".size()
===> 5
groovy:> "abcde".length()
===> 5

groovy:> "abcde" == "ab" + "c" + "de"
===> true

groovy:> "abcdeabcde" - "abc"
===> deabcde
Dla stringów zdefiniowano kilka ciekawych operatorów i funkcji.
Operator == porównuje względem wartości, a nie referencji jak w Java.
Groovy dekoruje istniejące klasy Java (jak string, tablice, kolekcje) dzięki czemu można wszędzie używać jednej metody size() zamiast raz size, a raz length.
Stringi można też odejmować - zostaje usunięte pierwsze wystąpienie odejmowanego napisu.

Dodatkowe informacje o String w Groovy.

czwartek, 24 lipca 2008

PDD - uznana metodyka wytwarzania oprogramowania

PDD - Presentation Driven Development ;)

Zasady pierwotnej wersji PDD:
  • Błędów się nie usuwa, to zajmuje za dużo czasu, błędy się omija lub wstawia dane na sztywno
  • Nie rozpatruje się przypadków brzegowych, żadnych
  • Nie rozpatruje się nic, czego nie można pokazać - np. wyjątki - nigdzie się ich nie łapie, albo łapie wszystkie i nie pokazuje, że coś poszło nie tak
  • Działanie nie musi być stabilne, a zarządzanie pamięcią w ogole istnieć - aplikacja ma tylko działać przez czas prezentacji
  • Jeśli jakieś zadanie zajmuje dużo czasu co powoduje, że oglądający prezentacje lub uruchamiający aplikację musi czekać, to dane do tego zadania wstawia się na sztywno lub wcześniej przygotowuje wyniki (nie konieczne będące efektem działania programu)
  • Kontrolki i ekrany muszą być ładne, najlepiej lekko animowane
  • Każdy przycisk ma znaczącą ikonke
  • Tworzy się jedyną ścieżkę krytyczną - ona musi wyglądać jakby działała i ją sie pokaże
  • Nie dba się o jaką kolwiek zgodność wsteczną (ale można przygotować port danych ze starej wersji i pokazać jak wygląda w nowej (mówiąc, że dane pochodzą ze starej :) ))
Niektóre firmy postanowiły zaadaptować PDD również dla oprogramowania, które trafia do klientów, wtedy pojawiają się pewneo zmiany (obsługuje się niektóre przypadki brzegowe), zasady pozostają podobne. Kilka zmian w PDD dla oprogramowania dostarczanego użytkownikowi:
  • Tworzy się kreatorki ułatwiające pracę na początku, nawet jeśli na dalszych etapach unimożliwiają pracę z powodu złożoności zagadnienia lub ilości danych
  • Oprogramowanie ma dobrze wyglądać i bardzo dobrze się sprawdzać dla małych prostych problemów, powinny być dla nich zaproponowane rozwiązania, nie muszą być ogólne
  • Nie dba się o skalowalność, czy złożone przypadki
  • Nie dba się o kompatybilność między wersjami np. wersja 2.0 jest nie kompatybilna z 1.1, a ta jest absolutnie nie zgodna z 1.0
  • Po wydaniu oprogramowania zbiera się maksymalnie wszelki feedback od użytkowników i zmienia się wersje pierwotną nawet, jeśli użytkownicy chcą zupełnie co innego niż zaplanowano, jeśli wystarczająco duża liczba użytkowników chce coś to można stworzyć kilka różnych produktów

Niektóre duże firmy z powodzeniem stosują tą metodykę. Jej najefektywniejsza forma jest nazwana $DD - Money Driven Development - stosuje się PDD, ale pierwsza wersja oprogramowania jest dokładną kopią cudzego produktu. Reszta zasad pozostaje bez zmian.

Nie będe oceniał tutaj PDD i $DD. Pamiętać trzeba tylko o jednym - jeśli firma korzysta z $DD to nigdy nie należy korzystać produkcyjnie z pierwszej wersji oprogramowania, a wersję 2.0 stosować tylko w razie potrzeby po przetestowaniu w rzeczywistym środowisku. Przeważnie wersja 3.0 jest już bardzo udanym produktem.

ps. Pierwszy raz zauważyłem stosowanie PDD w projekcie tworzonym przez studentów na konkurs, później zauważyłem komercyjne wykorzytanie odkrytej metodyki. Przypomniałem sobie o PDD z okazji Entity Framework i LINQ to SQL.

wtorek, 22 lipca 2008

Języki na JVM - Scala

Scala jest językiem, który moim zdaniem zyska dużo zwolenników. Podobnie jak Groovy został stworzony od podstaw i oparty na Javie. Również tutaj klasy Scala są poprawnymi klasami Java i dziedziczenie jest możliwe. Jednak wzoruje się na językach funkcyjnych takich jak SML i Ocaml, jest silnie, statycznie typowany i to bardzo odróżnia język od JRuby, Jython i Groovy.

Scala
  • Silna i statyczna typizacja - czyli po prostu jest i będzie szybszy niż 3 wcześniej wymienione, a narzędzia potencjalnie mogą być lepsze
  • Odgadywanie typu (type inference) - zdecydowanie mniej gadatliwy kod niż w Java
  • Czysto obiektowy (absolutnie wszystko jest obiektem)
  • Silnie funkcyjny z ładną obsługą powiązanych koncepcji
  • Przeciążanie operatorów
  • Pozwala na pewnego rodzaju wielodziedziczenie (mixin,trait)
  • Wzorzec singleton jest elementem języka
  • Kompilowany do bytecodu
  • Bezpośrednie korzystanie z typów Java
  • Możliwe dziedziczenie po klasach Java
  • W przyszłości będą możliwe cykliczne zależności tak jak w Groovy
  • Podobno ma lepsze generics niż Java :)
  • Obsługują adnotacje
  • Podsumowując możemy używać wszędzie tam gdzie Java, np. w JPA / Hibernate
  • Wspiera programowanie współbieżne (dzięki aktorom)
  • Jest i będzie szybszy niż Groovy, Ruby/JRuby, Python/Jython, JavaScript etc.
  • Istnieje kompilator Scala dla .Net
Przykładowe aplikacje w Scala: Lift

Język od początku został zaplanowany jako język funkcyjny, co to oznacza ? Szybki (mimo, że w kompilatory języków funkcyjnych nie zainwestowano tyle czasu co w kompilatory C/C++ to i tak zawsze są blisko czołówki w rankingach wydajności), łatwiejsza obsługa wielordzeniowości, język ładny, kompaktowy, naturalny w obliczeniach matematycznych...
def qsort(lst: List[Int]):List[Int] =
lst match {
case Nil => Nil
case pivot::tail => qsort(tail filter { _ < pivot }) ::: pivot ::: qsort(tail filter { _ >= pivot })
}
Oto definicja funkcji (w Scali do zmiennej można przypisywać funkcje, stąd taka notacja). Oczywiście algorytm to QuickSort. Teraz bardziej znajoma encja JPA:
@Entity
class SomeClass {
@Id @Generated
var id: Long = -1
var name: String = _
}
Jest dużo różnic między Java a Scala, bardzo dużo. Prawie każda koncepcja jest zmieniona lub nowa. Krzywa nauki jest z tego powodu stroma. Przy okazji jeszcze różnice między Java Groovy i Ruby.
Studiując różnice nie da się zauważyć, że najłatwiej zrozumieć Groovy, można go wprowadzać stopniowo zaczynając od Java, albo uczyć się od podstaw od razu w nowym stylu. Pewnie dlatego będzie cześciej spotykany od Scali, mimo że Scala jest/będzie szybszym, gotowym na wielordzeniowość - ogólnie scalowalnym językim. Może jednak wprowadza zbyt wiele zmian? Czy zwykłego szarego programiste można nauczyć Scali?

Wszystkie 4 dotychczas opisane języki posiadają konsolę - interpreter na bieżąco wykonujący podany mu kod, dzięki czemu możemy błyskawicznie uczyć się poznawać konstrukcje języków.

Jaki powinien być dobry język według mnie? Powinien być silnie i statycznie typowany z refleksją i odgadywaniem typów wszędzie gdzie to możliwe, mieć spójny model semantyczny, który jest zamienialny na sensowny diagramy akcji i sekwencji, pozwalać na styl funkcyjny, mieć domknięcia i definicję rozszerzeń języka.
Myślę, że pojawi się więcej lub zyskają na popularności języki funkcyjne lub tylko trochę "brudne obiektowo" lub nawet czysto funkcyjne, które będą bezpieczne we wszelkich obliczeniach równoległych (więc w sam raz na serwery web, usługi sieciowe, komunikatory i desktopy skoro za parę lat będziemy mieli 64+ rdzeni). Już widać powrót i ponowne wykorzystanie starego języka Erlang. Pewnie będą próby tworzenia systemów operacyjnych w takich językach. ok, kolejne posty będą bardziej praktyczne.

Języki skryptowe dla JVM - Groovy, Python, Ruby

Maszyna wirtualna Java jest świetną platformą, która udowodniła, że radzi sobie nieźle z nowymi językami. Moim zdaniem na razie liczą się tylko (aż?) 3 języki Jython, JRuby i Groovy. W przyszłości na pewno dołączy do nich Scala (wiki). Są to dojrzałe implementacje, czego nie można powiedzieć o odpowiednikach na platformę .Net.

Cechy wspólne języków
Jython, JRuby, Groovy
  • Mogą być kompilowane do bytecode
  • Moga być interpretowane za równo z konsoli jak i w programach Java z plików lub zwykłego String
  • Mogą wykorzystywać a nawet dziedziczyć po klasach Java
  • Są to języki dynamiczne
  • Pozwalają na przeciążanie operatorów
  • Mapy, listy itp to natywne konstrukcje języka (np. [a: 1, b: 2] to mapa)
  • Pozwalają na pisanie w stylu funkcyjnym
Są to języki z typizacją w stylu "Duck Typing". Troche więcej na ten temat w stopce.

Co ciekawe Jython, JRuby są szybsze niż ich natywne wersje pisane w C. Można porównać testy. W tych testach polecam nie patrzyć na języki spoza JVM, ponieważ procedura testowa nie jest do tego odpowiednia (jak w prawie każdych micro testach). Przy okazji warto zauważyć gdzie jest Scala względem pozostałych trzech i Java.

Jython
Stara sprawdzona implementacja języka Python na JVM i robi to dobrze. Python to jeden z 3 "dozwolonych" języków w Google (obok Java i C). Cecha szczególna - wcięcia zamiast nawiasów klamrowych. Google App Engine obsługuje jak na razie tylko Python.

Znane aplikacje Python: Django, Plone, Zope, Trac (ostatni warty uwagi nawet jeśli nie chcemy pisać w Python).

JRuby
JRuby zapewnia bardzo dobrą kompatybilność z Ruby. Ruby to wcale nie taki młody język, kiedyś znany właściwie tylko w Japonii. Praktycznie całą aktualną popularność zawdzięcza powstaniu webframeworka Ruby on Rails. Sun wpiera implementację JRuby (zatrudnił na stałe kilku jego programistów). Na polskiej Wikipedii znajduje się dobry opis Ruby.

Znane aplikacje Ruby: Ruby on Rails, Basecamp

Zarówno JRuby jak i Jython dostarczają bazowe biblioteki znane z natywnych wersji swoich języków jak i pozwalają korzystać z bibliotek Java.

Groovy
Groovy nie jest portem istniejącego języka, został pomyślany od początku jako "nowa lepsza Java". Większość kodu napisanego w Javie jest jednocześnie poprawnym(choć strasznie przegadanym) kodem Groovy.

Groovy ma najlepszą integrację z Javą:
  • Może być silnie typowany, ale nie musi
  • Klasy Groovy po kompilacji to stare dobre klasy Java, ale nie każdą metodę można wywołać bezpośrednio (w końcu to język dynamiczny, więc pewne metody nie istnieją w trakcie kompilacji)
  • Klasy JDK mają dodatkowe metody, zwykle są one szalenie przydatne
  • Jako jedyny pozwala na dwustronne dziedziczenie (Java -> Groovy i Groovy -> Java) (a odkąd wspiera łączoną kompilację Java + Groovy, możemy mieć cykliczne zależności (dziedziczenie, zawieranie, korzystanie z metod) między klasami Java i Groovy, w Scali też kiedyś pojawi się taka możliwość).
  • Z tych 3 języków tylko Groovy ma możliwość używania adnotacji i generics, a klasy Groovy są poprawnymi klasami Java, czyli możemy sobie oznaczyć klasę Groovy jako "@Entity" i używać dla nich Hibernate - to wykorzystuje np. Seam)
Znane aplikacje w Groovy: Grails (w pewnym sensie (koncepcyjnie) odpowiednik Ruby on Rails, wykorzystuje m.in. Spring i Hibernate)

Gdy się zna Java nie skryptowego języka prostszego do nauki niż Groovy. Nauka może przebiegać w małych krokach - zmieniamy rozszerzenie pliku klasy Java na groovy, a później usuwamy niepotrzebny kod dzięki jakiejś nowo poznanej opcji. Wydaje mi się, że nie pozwala również na klasy wewnętrzne i anonimowe (nie pamiętam teraz). Groovy namiętnie używa pewnej koncepcji nazwanej "builders". Są to takie mini języki tworzące w banalny sposób wszelkie struktury drzewiaste, czyli XML, Swing, SWT, JSP, ANT... Jest to możliwe w innych językach - tu jest jedną z podstawowych koncepcji i ładnie wygląda. Jeśli utknęło się na starej wersji Java np. 1.4.2 to dalej można wykorzystać większość opcji z Groovy (bez np. adnotacji itp.).

Jeszcze troche o typizacji
O typizacji mówi się czasem, że jest słaba lub silna, albo statyczna lub dynamiczna, jest z tym sporo zamieszania i nie mam zamiaru opisywać wszystkiego.
Groovy potrafi udawać statyczną typizacje znaną z Java. Domyślnie tak jak reszta ma dynamiczną typizację, w formie nazywanej "Duck Typing" mi ten termin bardziej by pasował do Ocaml, no ale trudno - tak się przyjeło. W skrócie wszystkie trzy języki pozwalają na przesyłanie czegokolwiek, gdziekolwiek i tylko w trakcie wykonania mogą się poskarżyć, że dany typ jest nie taki bo nie ma funkcji, którą staramy się wywołać.

Przykład z http://onestepback.org/articles/groovy/ducktyping.html
class Duck {
quack() { println "I am a Duck" }
}

class Frog {
quack() { println "I am a Frog" }
}

quackers = [ new Duck(), new Frog() ]
for (q in quackers) {
q.quack()
}
Kod jest poprawny według zasady, że "jeśli coś wygląda jak kaczka i zachowuje się jak kaczka to jest kaczką".

Można powiedzieć, że tak naprawdę nie wywołujemy metody na obiekcie ale przesyłamy komunikat do obiektu z nazwą funkcji i parametrami, a sam obiekt stara się coś z tym zrobić. Dzięki temu możemy obsługiwać funkcje, których nie ma (powiedzmy, że przeciążamy funkcję MethodMissing i tam każemy dla funkcji o sygnaturze hello_xxx wypisywać xxx na ekran). Pozwala to na tworzenie doskonałych bibliotek o bardzo czytelnym kodzie, ale jednocześnie powoduje, że podpowiadanie składni, statyczna analiza kodu, automatyczne wyszukiwanie błędów, czy zwykła nawigacja po kodzie nigdy nie będą tak dobre jak to jest możliwe w Java czy C#. Jednocześnie m.in. z tego powodu języki te zawsze będą wolniejsze niż Java (istnieje dodatkowy narzut na wykonanie każdej funkcji, utrudnia to też pracę JIT).
Tu znów pewnym wyjątkiem jest Groovy, który pozwala na statyczną typizację, tylko nie wiem czy z tego korzysta do optymalizacji. Z drugiej strony jest jedynym z wymienionych języków, który natywnie wspiera Multiple Dispatch, czyli multimethods(dobry krótki przykład) jak to nazywają w Groovy, a to wymaga badania rzeczywistego typu każdego parametru funkcji i ponownie zmniejsza wydajność kodu.

Wracając do Ocaml - tu typizacja jest silna i statyczna (z odgadywaniem typów) mimo to kod często przypomina języki skryptowe. W Ocamlu istnieje coś jak dziedziczenie choć są pewne różnice. Dalej obiekt jest zgodny z jeśli mają te same metody (w uproszczeniu). Ciekawie to działa w praktyce (tak jak na poprzednim przykładzie ale zwróciło by błąd w trakcie kompilacji, gdyby któraś z klas nie zawierała metody quack()). Typy choć na codzień tego się nie dostrzega to dość złożone zagadnienie, dalszych informacji można szukać w google i na wikipedii pod hasłami covariance i contravariance. Przydatne do paru problemów z generics i ze zwykłymi tabelami.

w następnej części opis Scali.
Już teraz specjalnie dla Anke: The Cake Pattern.

wtorek, 15 lipca 2008

Blogger i Syntax Highlighter

Niech pojawia się kolorki.
<pre name="code" class="java">
//Komentarz
public Klasa{
public static void main(String args[])
{
System.out.println("Hello World!"/*jakie to oryginalne*/);
}
}
</pre>

Zamieni sie na:

//Komentarz
public Klasa{
public static void main(String args[])
{
System.out.println("Hello World!"/*jakie to oryginalne*/);
}
}


Można to osiągnąć poprzez odwołanie do kodu SyntaxHighlighter z szablonu bloggera. Przechodzę do edycji szablonu bloga (Dostosuj -> Układ -> Edytuj Kod HTML).
  1. Najpierw potrzebne mi jest miejsce w sieci, gdzie umieszczę SyntaxHighlighter np. google pages, app engine lub dowolny inny darmowy hosting. Można też wykorzystać istniejące lokalizacje, próbowałem również odwołać się do SVN projektu, jednak plik css coś nie chciał współpracować, dlatego korzystam z googlepages.

  2. W kodzie html szablonu dodaje kod z odwołaniami do skryptów dla odpowiednich języków (umieszczam kod zaraz przed </body>). Dodatkowe języki (Scala, Bash etc.) mozna znaleźć w sieci.

<link href="'http://toolsmatter.googlepages.com/SyntaxHighlighter.css'" rel="'stylesheet'" type="'text/css'/">
<script language="'javascript'" src="'http://toolsmatter.googlepages.com/shCore.js'/">
<script language="'javascript'" src="'http://toolsmatter.googlepages.com/js/shBrushScala.js'/">
<script language="'javascript'" src="'http://toolsmatter.googlepages.com/shBrushXml.js'/">
<script language="'javascript'" src="'http://toolsmatter.googlepages.com/shBrushShell.js'/">
<script language="'javascript'" src="'http://toolsmatter.googlepages.com/shBrushJava.js'/">
<script language="'javascript'" src="'http://toolsmatter.googlepages.com/shBrushSql.js'/">
<script language="'javascript'" src="'http://toolsmatter.googlepages.com/shBrushJScript.js'/">


<script language="'javascript'">
dp.SyntaxHighlighter.ClipboardSwf = 'http://toolsmatter.googlepages.com/clipboard.swf';
dp.SyntaxHighlighter.BloggerMode();
dp.SyntaxHighlighter.HighlightAll('code');
</script>
Minus takiego podejścia jest prosty - zmiana szablonu wymaga odrobine pracy. Nie zmienia się szablonu często więc jest to ok. Jednak jedno wklejenie kodu uświadamia mi, ze ten blog potrzebuje szerszego szablonu.

ps. w przypadku kodu xml / html, trzeba zamieniać przed wysłaniem '<' na '&lt;'

piątek, 11 lipca 2008

Blog i kolorowanie kodu

Kobiety widzą kolory. A mężczyźni ? Dla mężczyzn łososiowy to yyy smak, fiołkowy może być zapach, pomarańcza to owoc, a kremowy? Masz na myśli kremową konsystencje chyba?
No dobrze, może płeć mniej piękną nie potrafi doradzić, czy do niebieskich oczu lepiej pasują lazurowe, czy turkusowe kolczyki jednak kilka kolorów nazwać umiemy! Podobno statystycznie od 6 do 8, a zdarzają się tacy co dobiją do 16. Pomijam oczywiście w statystyce artystów, im pokrewnych i spowinowaconych.

A ile kolorów rozróżniają programiści ?

Hmm, String, komentarz, identyfikator, słowo kluczowe, operator, liczba. Razem 6 - jak widać nie odbiegamy od przeciętnej.

Dobrze, a jak zmusić by na blogu kod był w kolorach?
  1. Kopiować kolorowy kod z edytora (jako HTML)
  2. Skorzystać z uniwersalnego skryptu zamieniającego kod na pokolorowany HTML
  3. Skorzystać ze skryptu JavaScript, który koloruje kod po stronie klienta
ad 1.
Używany edytor kodu musiałby umieć bezpośrednio zapisać kod w HTML lub (co częstrze) pozwolał na skopiowanie kolorowego kodu np. do Word, z którego można wysłać notkę bezpośrednio na blog lub zapisać do HTML. W takim rozwiązaniu każdy język będzie najpewniej miał własny styl kolorowania.

ad 2.
np. Quick Highlighter który obsługuje znacznie więcej języków niż jestem w stanie nazwać. Nawet Apache Log File, Lotus Script, czy robots.txt ! A zresztą po co pisać - menu skopiowane ze strony:

Select a language



Z ciekawych opcji - nazwy klas dla Javy mają automatycznie przypisany link, który pozwala na wyszukiwanie ich w google. Przy takim rozwiązaniu styl jest konsekwenty, a linie ponumerowane.

ad 3.
Najwygodniejsze rozwiązanie, po integracji wystarcza jeden tag, wybór języka i sam kod.
Czyli w przybliżeniu taki html:

<kod jezyk="obslugiwanyJezyk">
printf "Hello World"
...
</kod>

Zamienia się na kolorowy kod. I to wszystko na koszt klienta.
Właśnie tak działa SyntaxHighlighter, istnieją pluginy do WordPress i innych platform, które pozwalają na prostą integrację z blogiem. Są też inne rozwiązania np. highlight.js, czy google-code-prettify. Nie widze pluginu do Bloggera, wieć następny post będzie o integracji wybranego skryptu z Bloggerem.

init()

Koniec sesji to dobra okazja by stworzyć blog. Ten ma na celu pewną systematyzację moich poczynań przy zabawie, a czasem walce z technologiami.

W związku z moją pracą dyplomową, na blogu najpewniej pojawi się sporo z zakresu języków dziedzinowych, modelowania, transformacji między modelami, tworzeniu kodu na podstawie modeli etc. Temat jest spory, a blog wymusza pewien porządek, jest trwały i może przekona mnie do systematycznej pracy.

Poniżej lista skrótów, które mają szansę się pojawić, jeśli ktoś jest zainteresowany jakimś tematem to czekam na sugestie.

! - prawie pewne
? - mało prawdopodobne

Modelowanie
  • UML! czyli najlepszy i najgorszy zarazem język do modelowania
  • Ecore
  • openArchitetureWare!
  • ATL?
  • Eclipse EMF, bo całe Eclipse jest Model Driven!
  • ADM, MDA na opak, czyli jak z istniejącego kodu coś wyciągnąć, albo przetłumaczyć na inny
DSL*
  • GMF, czyli własny język w 2 tygodnie
  • MS DSL Toolkit, czyli własny język w tydzień
  • ANTLR, czyli własny język w weekend
  • oAW XText, czyli własny język w godzinę
* Czasy nie poparte żadnymi badaniami ;)

Web
  • JSF + Seam! lub Shale?
  • Struts2
  • Spring MVC + WebFlow
  • Stripes
  • Wicket?
  • ASP.NET MVC
  • Flex
  • GWT
Frameworki
  • Spring IoC
  • Eclipse RCP
  • Google App Engine
  • ADO.NET Entity Framework, czyli czego w Javie się nie da, co da się w C# i czemu należy czekać na wersję 2.0 w przypadku produktów Microsoft
Języki
  • Groovy! po co generować setery...
  • Scala? bo silna typizacja to lepsze (potencjalnie) narzędzia i prędkość
  • C# 3.0, styl funkcyjny i jak zrobić własny Linq, czyli dlaczego C# zaczyna mi się podobać
  • Erlang? bo i tak będziemy mieli kilkadziesiąt rdzeni | najprostszy z języków ?
  • AspectJ? bo aspekty są dobre
Toolbox
  • Małe i duże narzędzia przydatne na co dzień
Wygląda na to, że tematów starczy mi na kilka najbliższych lat...
Lista pozwoli na pamiętanie pomysłów, a blog ma też na celu uświadomienie mi, że niestety wszystkiego poznać i opisać nie można...

Teraz pora na wybór.