wtorek, 19 lipca 2011

Jaki jest twój japoński adres?

Ostatnio dowiedziałem się o fakcie, który nieźle zamieszał mi w głowie... 

Wyobraźmy sobie dwie sytuacje.

Sytuacja 1.

Stoisz przed Galerią Centrum na ulicy Marszałkowskiej w Warszawie i czekasz na znajomego. ­W pewnym momencie podchodzi do ciebie Japończyk i pyta się "Jaki jest numer tego kwartału?" (czerwony znak zapytania).


Nieco zmieszany, prosisz go, aby powtórzył pytanie, bo nie do końca zrozumiałeś o co chodzi. Azjata powtarza i wyjaśnia, że chciałby poznać adres miejsca w którym razem teraz stoicie. Załapałeś o co chodzi i kompleksowo zaczynasz mu opisywać - "Znajdujesz się przed budynkiem Galerii Centrum Wars-Sawa, który ograniczony jest przez ulicę Marszałkowską, Złotą i Chmielną..." Japończyk przerywa ci z miną wyrażającą nieporozumienie i prosi o powtórne wyjaśnienie. Zaczynasz się trochę irytować, ale powtarzasz to samo co powiedziałeś przed chwilą. Widzisz jednak, że turysta dalej nie ma pojęcia o co chodzi, ale odchodzi, nie chcąc sprawiać ci dalszego kłopotu.

Czy powiedziałeś coś nie tak?

Sytuacja 2.

Zgubiłeś się w Tokio. Jesteś na skrzyżowaniu (w punkcie oznaczonym czerwoną kropką) i pytasz się pierwszego przechodnia o swoje położenie, żebyś mógł je wpisać do mapy, którą masz w telefonie.



Pierwszy napotkany mieszkaniec, uśmiecha się i grzecznie odpowiada, że znajdujesz się w okręgu 5, między kwartałami 16, 17, 21 i 14.  Otwierasz szeroko oczy nie wiedząc co powiedzieć. Mieszkaniec widząc to zdziwienie powtarza to co powiedział, a następnie widząc twoje zmieszanie oferuje pomoc we wprowadzeniu lokalizacji do telefonu. Zerkasz na wyświetlacz... Faktycznie, działa ...

Ale co za cyferki, o których gadał ten skośnooki?

Dlaczego?

Japoński system adresowy różni się od zachodniego. Nazwy ulic są praktycznie nieużywane( z wyjątkiem starych części miast i słabo zaludnionych wiosek).  Adres zapisuje się stopniowo, od największej jednostki geograficznej (prefektury) do najmniejszej (numeru mieszkania).

Prefektury są jednostkami administracyjnymi, czymś w rodzaju polskich województw. Są większe niż powiaty, miasta i wsie. Każdą z nich rządzi gubernator i jednoizbowy parlament. Japonia składa się z 47 j.a. (43 prefektur, 1 metropolii - Tokio, 1 dystryktu - Hokkaido, 2 prefektur miejskich - Osaka i Kioto).
Po prefekturze w adresie wymienia się miejscowość lub dzielnice które posiadają status miasta. Następną częścią adresu jest lokalizacja w ramach miejscowości - dzielnice.

Ostatnie trzy części adresu to numer okręgu miejskiego, kwartału i w końcu domu. Dwa pierwsze numery przydzielane są w zależności od oddalenia od centrum miejscowości. Ostatni (numer domu) zgodnie z ruchem wskazówek zegara wewnątrz kwartału w którym się znajduje.

Mnemotechnicznie:
Prefektura -> miasto -> dzielnica -> nr okręgu -> nr kwartału -> nr domu

Przykład.


Tokyo Central Post Office
2-7-2 Marunouchi, Chiyoda-ku
Tokyo 100-8799.


W tym adresie Tokyo to prefektura, Chiyoda-ku jeden z okręgów. Marunouchi (numer 2) jest nazwą okręgu miejskiego. Ostatnie dwie cyfry (7-2) symbolizują kwartał i numer domu. W ostatnim wierszu mamy podany kod pocztowy składający się z trzech cyfr dywiz i 4 indywidualnych cyfry przydzielanych urzędowi pocztowemu.

Amazing, huh? :)

wtorek, 5 lipca 2011

Jak tworzyć klasy w języku JavaScript?

JavaScript nie stosuje rozwiązań znanych z popularnych języków umożliwiających programowanie obiektowe jak C++, Java czy PHP. Niewiele osób wie, że dzięki sprytnemu wykorzystaniu funkcji i zmiennych stworzymy implementację klasy, zawierającą konstruktor, składowe i metody, które mogą być publiczne, prywatne bądź też statyczne.

Jest to możliwe, ponieważ funkcje w języku JavaScript mają status obiektów pierwszej klasy, co oznacza, że funkcja traktowana jest równorzędnie z innymi typami danych (dzięki czemu może być zapisana w zmiennej lokalnej, przekazywana jako argument do innej funkcji czy zwracana jako jej wynik). 

Oto przykład przypisania zdefiniowanej funkcji do zmiennej, a następnie jej wywołanie:
// Przypisanie funkcji DisplayGreeting(hour) 
// do zmiennej display
var display = function DisplayGreeting(hour) {
 if (hour >= 22 || hour <= 5)
  document.write("Czas spac!");
 else
  document.write("Pracuj dalej!")
}

// Wywolanie funkcji DisplayGreeting() 
// poprzez zmienna display
display(10);
Jeśli decydujemy się przechowywać fragment kodu w zmiennej, warto rozważyć stworzenie "funkcji anonimowej", czyli bez nazwy. Tworzy się ją poprzez usunięcie nazwy z definicji.
var display = function (hour) {
...
}

display(10);
Funkcje anonimowe wykorzystuje się do przekazywania do ciała funkcji, w postaci argumentu, fragmentów kodu niewykorzystywanego w innym miejscu programu.

Wiedząc już, że funkcja będzie pełniła rolę obiektu zacznijmy od stworzenia konstruktora.

Konstruktor
Konstruktor to funkcja zdefiniowana wewnątrz klasy, która inicjuje obiekt w chwili jego tworzenia. Wywoływana jest ona automatycznie w chwili inicjacji obiektu za pomocą operatora new.
var myHello = new showHelloWorld();
Próba powołania instancji klasy (czyli tutaj obiektu myHello) skutkuje wywołaniem kodu zapisanego w funkcji, zupełnie tak, jakby nastąpiło jej bezpośrednie wywołanie.

W klasycznych językach programowania konstruktor jest specjalną publiczną metodą wewnątrz klasy. Nie zwraca ona żadnego wyniku, oraz jest uruchamiana w momencie tworzenia obiektu.

W języku JavaScript rolę konstruktora pełni kod funkcji będącej odpowiednikiem klasy
. Argumenty takiej funkcji są zatem parametrami konstruktora.

Zobaczmy przykład:
function helloWorld(hour) {
 // Konstruktor klasy inicjuje pole hour
 this.hour = (hour) ? hour : (new Date()).getHours();
 
 // Definicja funkcji wyswietlajacej pozdrowienie
 this.DisplayGreetings = function() {
  if (this.hour >= 22 || this.hour <= 5) 
   document.write("Czas spac!");
  else 
   document.write("Pracuj dalej!")
 }
}

var myHello = new helloWorld();
myHello.DisplayGreetings();

Tworzony jest obiekt klasy helloWorld. Klasa to posiada jedną publiczną składową - hour, która jeśli została podana, przyjmuje zadaną wartość, a jeśli nie - sprawdza aktualną godzinę w systemie. Obiekt ten posiada także jedną publiczną metodę (funkcję anonimową) o nazwie DisplayGreetings(), której zadaniem jest wyświetlenie komunikatu w zależności od godziny zapisanej w składowej.

Publiczne i prywatne składowe
W przykładzie wyżej klasa miała pola publiczne. Oznacza to, że każdy mógł odczytać bądź zmienić dane, odwołując się do nich np. w następujący sposób:
alert(myHello.hour); // wyswietla aktualną godzinę
Słowo this to odwołuje się do obiektu, na którym konstruktor jest wywoływany. Sposób ten pozwala na tworzenie publicznych zmiennych.
function Tablica(wiersze, kolumny) {
 // Składowe publiczne
 this.wiersze = wiersze;
 this.kolumny = kolumny;
}

var tab1 = new Tablica(2,4);
// wyświetla "2 4"
document.write(tab1.wiersze +  " " +  tab1.kolumny); 
Aby stworzyć prywatnego uczestnika klasy, będziemy musieli zadeklarować zmienną wewnątrz funkcji. Odbywa się to dzięki poprzedzeniu nazwy zmiennej deklaracją var (co znaczy tyle, że zmienna ma zasięg lokalny). Stworzone w ten sposób składowe są niewidoczne z poziomu instancji funkcji, w naszym przypadku możemy powiedzieć, że są to prywatne zmienne.
function Tablica(wiersze, kolumny) {
 // Składowe prywatne
 var wiersze = wiersze;
 var kolumny = kolumny;
}

var tab2 = new Tablica(5,2);
// wyświetla "undefined undefined"
document.write(tab2.wiersze + " " + tab2.kolumny);
Zmienne wiersze i kolumny obiektu tab2 nie są dostępne spoza ciała funkcji. Dzieje się tak, ponieważ składowe osiągane za pomocą słowa kluczowego this oraz deklarowane poprzez var przechowywane są w różnych miejscach pamięci. Wyjątkiem jest kontekst globalny, w którym nie wprowadza się podziału na zmienne (czyli składowe prywatne) i właściwości (składowe publiczne) obiektu.

Metody
Utwórzmy klasę Tablica, z publicznymi składowymi oraz z publiczną metodą zwracającą ilość komórek:
function Tablica(wiersze, kolumny) {
 // Składowe publiczne
 this.wiersze = wiersze;
 this.kolumny = kolumny;
 
 // Metody publiczne
 this.getLiczbaKomorek = function() {
  return this.wiersze * this.kolumny;
 }
}

var tab3 = new Tablica(1,5);
// wyświetla 5
document.write(tab3.getLiczbaKomorek());
Sposób jest intuicyjny i działa poprawnie, ale nie jest najlepszym rozwiązaniem. Kod zapisany w takiej formie, w chwili zainicjowania obiektu, stworzy nowe zmienne zawierające liczbę wierszy oraz kolumn, ale stworzy także nową kopię metody getLiczbaKomorek(), której nie potrzebujemy. Problem ten nazywany jest "niewystarczającym modelem obiektowym JavaScript". Wyobraźmy sobie, utworzenie kilku tysięcy obiektów klasy Tablica. Każda taka instancja zawierała będzie kod metody w niej zadeklarowanej. Takie praktyki nie najlepiej świadczą o roztropności programisty, ponieważ nie dba on o pamięć zużywaną przez jego aplikację. Stan obiektu musi być inny dla każdego z nich, ale metody klas mogą być wspólne. Jak tego dokonać? Wystarczy odwołać się do funkcji zewnętrznych...
function Tablica(wiersze, kolumny) {
 // Składowe publiczne
 this.wiersze = wiersze;
 this.kolumny = kolumny;
 
 // Metody publiczne
 this.getLiczbaKomorek = getLiczbaKomorek;
}

function getLiczbaKomorek() {
 return this.wiersze * this.kolumny;
}

var tab4 = new Tablica(3,15);
// wyświetla 45
document.write(tab4.getLiczbaKomorek());
To co zrobiliśmy to utworzenie referencji do zewnętrznej funkcji. Teraz wszystkie obiekty klasy Tablica będą korzystać z tej samej funkcji. Takie rozwiązanie jest znacznym ulepszeniem, ponieważ oszczędza moc obliczeniową i pamięć komputera.

Istnieje jeszcze jeden sposób na utworzenie publicznej metody. Ten sam efekt uzyskamy używając "prototypów".

Czym są prototypy?
Są to narzędzia JavaScriptu pozwalające przyłączać lub przypisywać metody do "planu" funkcji. Metody dodane do klasy (funkcji) prototypowej nie powielają się w czasie tworzenia obiektów tej klasy. Metody i właściwości dodane do klasy za pośrednictwem prototypu są automatycznie udostępniane wszystkim jej instancjom.
  • Każda funkcja w języku JavaScript ma właściwość prototype, która jest jednocześnie niezależnym obiektem.
  • Aby dodać uczestników do funkcji prototypowej należy dodać ich do właściwości prototype tej funkcji.
  • Każdy obiekt typu prototype dysponuje właściwością constructor, która przechowuje wskaźnik do funkcji konstruktora.
  • Funkcje i zmienne tworzące konstruktor nie są dostępne z poziomu funkcji dodanych do jego prototypu.
  • Dodanie nowego uczestnika do prototypu sprawia, że staje się on natychmiast dostępny dla wszystkich obiektów (nawet dla tych, które już istnieją)
  • Funkcja prototypu może zyskać nowych uczestników dopiero po zdefiniowaniu jej ciała
Utwórzmy analogiczną klasę wykorzystując właściwość prototype.
function Tablica(wiersze, kolumny) {
 // Składowe publiczne
 this.wiersze = wiersze;
 this.kolumny = kolumny;
}

Tablica.prototype.getLiczbaKomorek = function () {
 return this.wiersze * this.kolumny;
}

var tab5 = new Tablica(31,15);
document.write(tab5.getLiczbaKomorek());
Są to dwa sposoby na tworzenie publicznych metod, które to mogą być wykonywane z dowolnego miejsca w kodzie.

Zajmijmy się teraz prywatnymi metodami. W przypadku zmiennych prywatnych użycie referencji do metody nie będzie możliwe, ponieważ działają one tylko w obrębie funkcji, w której zostały zadeklarowane. W takim przypadku mamy do wyboru dwie opcje. Pierwsza to funkcja inline zdefiniowana w konstruktorze, a druga to funkcja przypisana do zmiennej z deklaracją var. Obie działają tylko w zakresie swojej klasy.
function Tablica(wiersze, kolumny) {
 // Składowe prywatne
 var _wiersze = wiersze;
 var _kolumny = kolumny;

 // Prywatne metody
 
 // OPCJA 1
 function _zwrocKomorki() {
  return _wiersze * _kolumny;
 }
 // OPCJA 2
 var _zwrocKomorki1 = function() {
  return _wiersze * _kolumny;
 }

 // obie funkcje zwraca 465 
 document.write(_zwrocKomorki());
 document.write(_zwrocKomorki1());
}

var tab5 = new Tablica(31,15);

// blad: tab5._zwrocKomorki is not a function
document.write(tab5._zwrocKomorki());
Próba odwołania się do funkcji z zewnątrz klasy, zwraca do konsoli błąd tab5._zwrockKomorki is not a function.

W przypadku klasy z polami publicznymi, aby prywatne metody poprawnie działały musimy przypisać wartości do tymczasowych zmiennych.
function Tablica(wiersze, kolumny) {
 // Składowe publiczne
 this.wiersze = wiersze;
 this.kolumny = kolumny;

 // Prywatne metody
 // Stwórz tymczasowe zmienne
var zm1 = this.wiersze;
var zm2 = this.kolumny;

// OPCJA 1
function _zwrocKomorki() {
return zm1 * zm2;
}
// OPCJA 2
var _zwrocKomorki1 = function() {
return zm1 * zm2;
}

// obie funkcje zwraca 465 
document.write(_zwrocKomorki());
document.write(_zwrocKomorki1());
}

var tab5 = new Tablica(31,15);

// zwraca undefined
document.write(tab5.zm1);
// zwraca 31
document.write(tab5.wiersze);

Zmienne statyczne
Zmienne statyczne w danym bloku programu posiadają dokładnie jedną instancję, niezależną od ilości utworzonych obiektów. Definiuje się je jako właściwości funkcji.
function Czlowiek(imie,kraj) {
 
 Czlowiek.populacja++;
 // lub
 // this.constructor.populacja++;
 
 this.imie = imie;
 this.kraj = kraj;
}

Czlowiek.populacja = 0;

czlowiek1 = new Czlowiek("Milena","Polska");
czlowiek2 = new Czlowiek("Kate","USA");
czlowiek3 = new Czlowiek("Sofia","Francja");

// wyswietla 3
document.write(Czlowiek.populacja);
W konstruktorze zadeklarowano instrukcję, która inkrementuje właściwość funkcji Człowiek o 1, przy każdym wywołaniu. Poza tą "klasą", zmiennej statycznej populacja przypisano początkową wartość równą zero. Po utworzeniu trzech obiektów, jej wartość automatycznie się zwiększa.

Podsumowanie
  • Zmienne publiczne
    Deklarowane: this.nazwaZmiennej
    Mogą być czytane i zmieniane poza klasą.
  • Zmienne prywatne
    Deklarowane: var nazwaZmiennej
    Dostęp do niej możliwy tylko wewnątrz danej funkcji.
  • Metody publiczne
    Deklarowane: nazwaKlasy.prototype.nazwaMetody = function() { .... }
    Deklarowane: this.nazwaMetody = nazwaMetody; (poza funkcją musi znajdować się kod funkcji nazwaMetody())
    Dostęp poza klasą i w klasie.
  • Metody prywatne
    Deklarowanie: funkcja inline w obrębie konstruktora,
    Deklarowanie: var nazwaMetody = function() { .. }
    Dostęp tylko w zakresie kodu funkcji (klasy).
  • Zmienne statyczne
    Deklaracja poza klasą: nazwaKlasy.nazwaZmiennej  = .... ;
    Zmiany wewnątrz klasy (np w konstruktorze).