In search this blog

Loading

poniedziałek, 30 sierpnia 2010

JavaFX, słowo kluczowe bind, czyli wiązania w JavaFX



cdn ... javaFX czas iść dalej ;)

Wiązania - bind

JavaFX posiada specyficzne podejście do zmiennych. Co ja przez to rozumie mianowicie istnieje możliwość powiązania jakiejś zmiennej z innymi. I gdy nastąpi zmiana głównej wartości zmiennej, zmianie ulegają również zmienne powiązane z nią. Wiązać możemy funkcje, sekwencje, obiekty itp. Pokaże teraz na przykładzie jak będzie wyglądało proste wiązanie do zmiennej:

  1. var pozycja = 10;  
  2.   def x = bind pozycja;  
  3.   println("przed zmiana pozycji {x}");  
  4.   pozycja -= 3;  
  5.   println("po zmianie pozycji {x}");  
  6. // teraz dodajmy tutaj jeszcze jedna zmienna np s  
  7.   def s = bind x - 7;  
  8.   println("powtarzamy wartosc pozycji: {pozycja}");  
  9.   println("powtarzamy wartosc dla x: {x}");  
  10. // tu oczywiscie wiadome zachowanie s bedzie rowne 0  
  11.   println("jaka wartosc ma s: {s}");  
  12.   pozycja += 30;  
  13.   println("wartosc pozycji: {pozycja}");  
  14.   println("wartosc dla x: {x}");  
  15.   println("jaka wartosc ma s: {s}");  
przed zmiana pozycji 10
  po zmianie pozycji 7
  powtarzamy wartosc pozycji: 7
  powtarzamy wartosc dla x: 7
  jaka wartosc ma s: 0
  wartosc pozycji: 37
  wartosc dla x: 37
  jaka wartosc ma s: 30

Przy zmianie zmiennej pozycja następuje automatyczna zmiana dwóch następnych zmiennych, w tym przypadku mechanizm działa tylko w jedna stronę. Dlatego też specjalnie zmienne z dowiązaniem zdeklarowałem jako def, a nie var. Jest możliwość zdeklarowania takiej zmiennej jako var. Czemu tak nie zrobiłem, przecież w sumie i tak te zmienne ulegają zmianie ? Więc sprawa wygląda tak, w dokumentacji JavaFX napisane jest że pomimo możliwości deklarowania zmiennej z dwoma identyfikatorami zalecane jest używanie tylko def (ponoć w dalszych wersjach ma być tylko dozwolone def). Wiec już jakiś powód podałem, który wygląda dość rozsądnie. Ale zaraz nasuwa się pytanie czemu oni tak, a nie inaczej zalecają. Według mojego rozumowania dzieje się tak dlatego, że nie ma możliwości modyfikacji zmiennej bindowanej czyli np. x lub s. I deklarując taką zmienną jako def automatycznie przestrzegamy samych siebie przed popełnieniem błędu. Drugi przykład pokaże jak się zachowują wartości logiczne:

  1. var flagA = true;  
  2.   def flagB = bind not flagA;  
  3.   def flagC = bind not flagB;  
  4.   println("jesli flagA jest {flagA} to flagC jest {flagC}, \n"  
  5.           "ale zawsze do nich przeciwna bedzie flagB, flagB jest {flagB}");  
  6.   
  7. //teraz zmienimy jedna zmienna bedzie nia zmienna flagB  
  8. // i prosze patrzec co bedzie z reszta flag  
  9.  if(flagA){  
  10.      flagA = false;  
  11.  }  
  12.  println("jesli flagA jest {flagA} to flagC jest {flagC}, \n"  
  13.          "ale zawsze do nich przeciwna bedzie flagB, flagB jest {flagB}");  
  14.   
  15. // negujemy false  
  16.  if(not flagA){  
  17.      flagA = true;  
  18.  }  
  19.  println("jesli flagA jest {flagA} to flagC jest {flagC}, \n"  
  20.          "ale zawsze do nich przeciwna bedzie flagB, flagB jest {flagB}");  

Zachowanie zmiennych było teraz bez większych problemów do przewidzenia. I jak na razie pamiętamy że zmieniać możemy tylko i wyłącznie zmienną do której dowiązujemy inne zmienne.

jesli flagA jest true to flagC jest true, 
       ale zawsze do nich przeciwna bedzie flagB, flagB jest false
  jesli flagA jest false to flagC jest false, 
       ale zawsze do nich przeciwna bedzie flagB, flagB jest true
  jesli flagA jest true to flagC jest true, 
       ale zawsze do nich przeciwna bedzie flagB, flagB jest false

Teraz mały przykład dla zastosowań wiązania z sekwencjami, należy pamiętać, że jeżeli dowiązujemy dla zmiennej po indeksie w tablicy (bind tab[2]) to taka wartość będzie zawsze równa elementowi znajdującemu się na 3 pozycji w naszej sekwencji. Przykład:

  1. var suma = 2;  
  2.   var tab_pocz = [1 .. 5]; //[1,2,3,4,5]  
  3.   def tab_doc = bind tab_pocz; //[1,2,3,4,5]  
  4.   def index2 = bind tab_pocz[2];// wskazuje teraz 3  
  5.   var i=0;  
  6.   // [1,2,3,4,5]  
  7.   for(int in tab_pocz){  
  8.       tab_pocz[i++] = int + suma;  
  9.   }  
  10.   
  11.   println("tab_pocz = {tab_pocz.toString()} index2[2] = {index2}");  
  12.   println("tab_doc = {tab_doc.toString()} index2[2] = {index2}\n");  
  13.   
  14.    insert 3 into tab_pocz;  
  15.   println("tab_pocz = {tab_pocz.toString()} index2[2] = {index2}");  
  16.   println("tab_doc = {tab_doc.toString()} index2[2] = {index2}\n");  
  17.   
  18.    insert 2 after tab_pocz[2];  
  19.   println("tab_pocz = {tab_pocz.toString()} index2[2] = {index2}");  
  20.   println("tab_doc = {tab_doc.toString()} index2[2] = {index2}\n");  
  21.   
  22.    insert 1 before tab_pocz[2];  
  23.   println("tab_pocz = {tab_pocz.toString()} index2[2] = {index2}");  
  24.   println("tab_doc = {tab_doc.toString()} index2[2] = {index2}\n");  
  25.   
  26.    def tab_doc2 = bind for(int in tab_doc){int + suma};  
  27.   println("tab_pocz = {tab_pocz.toString()} index2[2] = {index2}");  
  28.   println("tab_doc = {tab_doc.toString()} index2[2] = {index2}");  
  29.   println("tab_doc2 = {tab_doc2.toString()} index2[2] = {index2}\n");  
  30.   
  31.    suma = 3;// wiazanie bedzie uruchomione  
  32.   println("tab_pocz = {tab_pocz.toString()} index2[2] = {index2}");  
  33.   println("tab_doc = {tab_doc.toString()} index2[2] = {index2}");  
  34.   println("tab_doc2 = {tab_doc2.toString()} index2[2] = {index2}");  
tab_pocz = [ 7, 6, 5, 8, 9 ] index2[2] = 5
  tab_pocz = [ 3, 4, 5, 6, 7 ] index2[2] = 5
  tab_doc = [ 3, 4, 5, 6, 7 ] index2[2] = 5

  tab_pocz = [ 3, 4, 5, 6, 7, 3 ] index2[2] = 5
  tab_doc = [ 3, 4, 5, 6, 7, 3 ] index2[2] = 5

  tab_pocz = [ 3, 4, 5, 2, 6, 7, 3 ] index2[2] = 5
  tab_doc = [ 3, 4, 5, 2, 6, 7, 3 ] index2[2] = 5

  tab_pocz = [ 3, 4, 1, 5, 2, 6, 7, 3 ] index2[2] = 1
  tab_doc = [ 3, 4, 1, 5, 2, 6, 7, 3 ] index2[2] = 1

  tab_pocz = [ 3, 4, 1, 5, 2, 6, 7, 3 ] index2[2] = 1
  tab_doc = [ 3, 4, 1, 5, 2, 6, 7, 3 ] index2[2] = 1
  tab_doc2 = [ 5, 6, 3, 7, 4, 8, 9, 5 ] index2[2] = 1

  tab_pocz = [ 3, 4, 1, 5, 2, 6, 7, 3 ] index2[2] = 1
  tab_doc = [ 3, 4, 1, 5, 2, 6, 7, 3 ] index2[2] = 1
  tab_doc2 = [ 6, 7, 4, 8, 5, 9, 10, 6 ] index2[2] = 1

W przykładzie widać, że zmiany w przypadku bind'owania sekwencji do sekwencji zachowują się podobnie jak to występowało wczesniej. W przypadku pierwszego bind'owania dokładnie widzimy że zmieniając jakąś wartość w sekwencji automatycznie zmianie ulega wartość znajdująca się na tym samym miejscu w sekwencji dowiązanej. W przypadku drugim gdzie mamy dowiązanie do indeksu 2, zmienna zawsze pokaże nam wartość znajdująca się pod tym indeksem nie zależnie czy rozszerzamy czy obcinamy sekwencje. Zaś ostatni z przypadków jest chyba najbardziej zaskakujący z punktu widzenia wyników. Innowacyjną rzeczą jaką tu mamy jest już sam fakt zastosowania pętli for. Która to, tak jakby mogła zwracać wartość. I drugi zastanawiający fakt, że do dowiązania dodaliśmy inną zmienną i poprzez zmianę samej tej zmiennej zostaje uruchomiony proces dowiązania. Mamy również wiązania z with inverse czyli działające również w odwrotną stronę. Tego typu wiązania możemy stosować tylko i wyłącznie do wiązań prostych, czyli zmiennej do zmiennej. Przykład pokaże to dokładnie:

  1. var poz = 10;  
  2.   var przesY = 5;  
  3.   var x1 = bind poz with inverse;  
  4. // tu nie mozna stosowac inverse  
  5.   def y1 = bind x1 + przesY;  
  6.   println("poz= {poz} przesY= {przesY} x1= {x1} y1= {y1}");  
  7.   
  8.   poz = 2;  
  9.   println("poz= {poz} przesY= {przesY} x1= {x1} y1= {y1}");  
  10.   
  11.   x1 = 15;  
  12.   println("poz= {poz} przesY= {przesY} x1= {x1} y1= {y1}");  
  13.   
  14.   przesY = 1;  
  15.   println("poz= {poz} przesY= {przesY} x1= {x1} y1= {y1}");  
  16.   
  17. // nie mozna rowniez tak:  
  18. // var xx = bind (if(poz == 7) x1 else przesY) with inverse;   
poz= 10 przesY= 5 x1= 10 y1= 15
  poz= 2 przesY= 5 x1= 2 y1= 7
  poz= 15 przesY= 5 x1= 15 y1= 20
  poz= 15 przesY= 1 x1= 15 y1= 16

Należy również zwrócić uwagę na deklaracje zmiennej x1. Teraz widzimy, że jeśli chcemy wiązania w obie strony to obie zmienne które są powiązane między sobą muszą być deklarowane jako var (obie zmienne się zmieniają). Wiązania funkcji wyglądają podobnie, i gdy tylko zmianie ulegnie jakiś parametr w funkcji, uruchamia się wiązanie aktualizując wartość funkcji. W następnym przykładzie chciałbym pokazać jak zachowują się obiekty podczas wiązań. I zademonstruje jeden z trików który ładnie jest opisany w książce JavaFX in Action. Mianowicie trik będzie zapobiegał przed utworzeniem nowego obiektu przez wiązanie.

  1. class Punkt{  
  2.       var punkt: String;  
  3.       var x: Integer;  
  4.       var y: Integer;  
  5.   
  6.       init{  
  7.             println("Utworzono {punkt} x: {x} y: {y}");  
  8.           };  
  9.   }  
  10.   
  11.   var someX:Integer = 2;  
  12.   var someY:Integer = 3;  
  13.   
  14.   def punkt1= bind Punkt{  
  15.       punkt: "Punkt 1"  
  16.       x: someX;  
  17.       y: someY;  
  18.   };  
  19.   
  20.   def punkt2: Punkt = bind Punkt{  
  21.       punkt: "Punkt 2"  
  22.       x: bind someX;  
  23.       y: someY;  
  24.   };  
  25.   
  26.   def punkt3: Punkt = bind Punkt{  
  27.       punkt: "Punkt 3"  
  28.       x: bind someX;  
  29.       y: bind someY;  
  30.   };  
  31.   punkt1;  
  32.   punkt2;  
  33.   punkt3;  
  34.   println("");  
  35.   
  36.   someX = 4;  
  37.   
  38.   punkt1;  
  39.   punkt2;  
  40.   punkt3;  
  41.   println("");  
  42.   
  43.   someX = 5;  
  44.   someY = 6;  
  45.   
  46.   punkt1;  
  47.   punkt2;  
  48.   punkt3;  
Utworzono Punkt 1 x: 2 y: 3
  Utworzono Punkt 2 x: 2 y: 3
  Utworzono Punkt 3 x: 2 y: 3

  Utworzono Punkt 1 x: 4 y: 3

  Utworzono Punkt 1 x: 5 y: 6
  Utworzono Punkt 2 x: 5 y: 6

Może na początek kilka słów wyjaśnień dotyczących budowy klasy. Jak widać po kodzie mamy zadeklarowane trzy pola w klasie Punkt, ich omawiać chyba nie ma za bardzo poco, gdyż nasza klasa nie jest aż tak skomplikowana. Zamierzam tylko zaznaczyć ze blok init będzie wywoływany za każdym razem gdy, tylko zostanie utworzona nowa instancja obiektu lub też obiekt zostanie zaktualizowany. Więc teraz możemy przejść do wiązań. Przy pierwszym wywołaniu trzech punktów mamy te same wyniki. Bez większych niespodzianek, trzy wywołania, trzy print'y. I właśnie teraz dochodzimy do ciekawszej rzeczy mianowicie punkt2 jak i punkt3 nie dały nam żadnej sensownej odpowiedzi, gdyż ich obiekty nie były aktualizowane po mimo wiązania. Wynika to z tego że wewnętrzny bind efektywnie zapobiega działaniu zewnętrznego bind, który miał za zadanie zmienić wartość zmiennej. Ale jak widać w przypadku trzecim pomimo że jedna z wartości miała bind wewnętrzny to mimo to obiekt został zaktualizowany. Ten trick dobrze jest zapamiętać jeśli mamy gdzieś ochotę uniknięcia zmiany wartości naszego obiektu. To tyle na temat wiązań.


stat4u stat4u

Brak komentarzy:

Prześlij komentarz

Powered By Blogger