반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-8-Data-Binding-and-Triggers >


목차

- 바인딩이란?(Binding Overview)
- 바인딩과 객체(Binding and Object)
- 바인딩과 함수(Binding and Function)
- 바인딩과 시퀀스(Binding with Sequence)
- Raplace Trigger


* 바이딩이란?

bind 라는 키워드는 바인드 구문의 값인 목적변수의 값(value of target value)과 연관시키는 작업이다. 바인드 구문은 몇몇 기본 형(type)이나 객체, 함수의 결과 값 혹은 구문의 결과값으로 사용될 수 있다. 일단 말로서 설명을 하려면 약간 복잡 하지만 자바에서의 값을 참조하는 것과 동일하다고 보면 된다. 다음 설명등을 통해 좀 더 명확한 개념을 잡을 수 있을 것이다.


* 바인딩과 객체

먼저 아래 예를 먼저 보자.

var x = 0;
def y = bind x;
x = 1;
println(y); // y 는 1
x = 47;
println(y); // y 는 47

위 예에서 변수 x를 선언하고, y를 선언해 x를 바인드 시킨다.  그리고 x 값을 바꿔보자 그리고 y값을 프린트 한다. 그럼 바뀐 x 값이 출력된다. 이는 바운드된 변수인 y의 값은 x값에 따라 자동으로 업데이트 되어 새로운 값으로 바뀌게 된다.

여기서 이전 레슨에 대한 기억을 상기해보자. 우리는 예전 Lesson 2를 통해 def는 일반적으로 값을 할당한 후 상수 처럼 사용되는 변수라 설명한 바 있다. 실제로 def 로 선언된 변수의 값을 바꾸면 컴파일시 에러가 떨어진다. 하지만 bind 라는 키워드를 이용한 바인딩의 결과 그 값이 변경이 가능해진 것이다.

그럼 이를 객체에 적용해보자.

var myStreet = "1 Main Street";
var myCity = "Santa Clara";
var myState = "CA";
var myZip = "95050";

def address = bind Address {
     street: myStreet;
     city: myCity;
     state: myState;
     zip: myZip;
};

println("address.street == {address.street}");
myStreet = "100 Maple Street";
println("address.street == {address.street}");

결과는 다음과 같다.

address.street == 1 Main Street
address.street == 100 Maple Street

코드에서 보듯 address 라는 객체의 street은 myStreet이라는 변수를 참조하게 되고, 결과적으로 myStreet의 값이 바뀜에 따라 address 객체의 내부 변수인 street 변수의 값이 바뀌게 되었다.

위 예에서는 클래스인 Address를 바인딩 하였으나 그 하부의 인스턴스 변수에 까지 영향을 미침을 알 수 있다.
위 코드는 아래와 같은 방식들로도 표현 가능하다.

def address = bind Address {
     street: bind myStreet;
     city: bind myCity;
     state: bind myState;
     zip: bind myZip;
};
def address = Address {
     street: bind myStreet;
     city: bind myCity;
     state: bind myState;
     zip: bind myZip;
};


* 바인딩과 함수

이전 레슨에서 간단하게 함수에 대해 배운적이 있다. 이번에 배워야 할 부분이 더 있다. 바로 바운드 함수(bound function)와 비바운드 함수(non-bound function) 이다.

아래 함수를 보자. 아래 함수는 x, y 좌표를 받아 Point 라는 객체를 리턴한다.

var scale = 1.0;
bound function makePoint(xPos : Number, yPos : Number) : Point {
     Point {
          x: xPos * scale
          y: yPos * scale
     }
}
class Point {
     var x : Number;
     var y : Number;
}

여기서 makePoint 가 바운드 함수 이다. 바운드 함수는 function 키워드 앞에 bound라 표시한다. 이때 bound와 앞서 배운 bind를 다르다. 혼동해선 안된다.

이제 위 코드를 활용한 예를 보자.

var scale = 1.0;

bound function makePoint(xPos : Number, yPos : Number) : Point {
     Point {
          x: xPos * scale
          y: yPos * scale
     }
}

class Point {
     var x : Number;
     var y : Number;
}

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);

myX = 10.0;
println(pt.x);

scale = 2.0;
println(pt.x);

위 코드의 결과는 아래와 같다.

3.0
10.0
20.0

위 결과를 보면 바운드 함수의 결과값인 Point를 바인딩한 객체 pt는 인자(argument)인 myX 값의 변화에 따라 그 인스턴스 변수 값이 달라짐을 알수 있다. 또한 scale 변수값의 변경에도 영향을 받음을 알 수 있다.

그럼 비바운드 함수 경우에는 어떨까? 위 makePoint 함수에서 앞에 bound라는 키워드를 제거하고 다시 테스트 해보면 결과는 다음과 같아진다.

3.0
10.0
10.0

scale 변수값의 변경에는 영향을 받지 않음을 알 수 있다. 비바운드 함수의 경우 함수의 인자값에 변경이 가해질때만 함수의 재호출(re-invoke)가 일어난다. scale 변수는 함수의 인자값이 아닌 함수의 외부 변수라 함수에 영향을 미치지 않음이다. 즉, 이말은 다음과 같다. 바운드 함수의 경우엔 함수의 인자나 함수에서 사용되어지는 변수의 바인드 되어지는 변수에 어떤 값의 변경이 이루어지면 내부적으로 함수의 재호출이 일어나 값이 바뀌게 되는 것이다.


* 바인딩과 시퀀스

bind는 for문에서도 사용 할 수 있다. 이를 보기전에 먼저 두개의 시퀀스를 정의하고 그 결과를 봐보자.

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
printSeqs();

function printSeqs() {
     println("First Sequence:");
     for (i in seq1){println(i);}
     println("Second Sequence:");
     for (i in seq2){println(i);}
}

seq1에는 1부터 10까지 10개의 아이템(item)이 있고, seq2에도 10개의 아이템이 있다. seq2의 아이템들은 seq1의 아이템에 각각 2를 곱한 값이다. 결과는 다음과 같다.

First Sequence:
1
2
3
4
5
6
7
8
9
10
Second Sequence:
2
4
6
8
10
12
14
16
18
20

위 코드를 보면 seq1과 seq2 는 bind 키워드를 통해 바인딩 된 상태이다.

그럼 이런 의문이 든다. "만약 seq1의 모든 아이템이 바뀌거나 혹은 몇몇 아이템이 바뀐다면 seq2에 영향을 미칠까?" 한번 테스트 해보자. seq1의 제일 뒤에 11이란 값을 인서트 해보자. 그리고 seq2를 프린트 해봄으로서 확인해보자.

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
insert 11 into seq1;
printSeqs();

function printSeqs() {
     println("First Sequence:");
     for (i in seq1){println(i);}
     println("Second Sequence:");
     for (i in seq2){println(i);}
}

결과는 다음과 같다.

First Sequence:
1
2
3
4
5
6
7
8
9
10
11
Second Sequence:
2
4
6
8
10
12
14
16
18
20
22

결과는 seq1의 끝에 11을 인서트 함으로서 seq2의 앞에 10개의 아이템에는 아무런 영향을 미치지 않고, seq2 끝에 22라는 값이 추가 되었음을 알 수 있다.


* Replace Trigger

Replace Trigger는 임의의 코드 블록으로 변수 뒤에 붙어 변수의 값이 바뀌었을때 실행된다. 아래의 코드가 기본 문법을 보여준다. password 라는 변수를 정의하고, 위에 replace trigger를 붙인다. password의 값이 변경되면 trigger를 통해 이 임의의 코드 블록이 실행된다.

var password = "foo" on replace oldValue {
     println("\nALERT! Password has changed!");
     println("Old Value: {oldValue}");
     println("New Value: {password}");
};

password = "bar";

결과는 다음과 같다.

ALERT! Password has changed!
Old Value: 
New Value: foo

ALERT! Password has changed!
Old Value: foo
New Value: bar
위 예제에서는 trigger가 두번 사용된다. 처음엔 password 변수가 초기화 되고 "foo"라는 값이 할당되었을 때 그리고 다시 "bar"라는 값이 할당되었을 때이다. 위 코드에서 사용된 oldValue라는 변수는 trigger가 호출되면 이전 값이 저장된다. 변수명은 여기선 임의로 oldValue라고 하였으나 정해진 것은 아니다.(사용자가 임의로 변경 가능하다.)

+ Recent posts