반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-10-Packages >

목차
- 1단계 : 패키지 이름을 선택한다.
- 2단계 : 디렉토리를 생성한다.
- 3단계 : 패키지 선언을 추가한다.
- 4단계 : 접근자를 추가한다.
- 5단계 : 소스를 컴파일한다.
- 6단계 : 클래스를 사용한다.

현재까지의 테스트는 아마 한 디렉토리내에서 이루어졌을 것이다. 그럼 다른 디렉토리에 있는 클래스는 어떻게 사용해야 할까? 정답은 바로 패키지이다. 패키지를 이용한 개선된 소스코드 관리 방식을 이용해보자.

패키지는 코드들을 기능적으로 분류하여 그룹화 시킨다. 패키지는 또한 정의된 클래스에 고유한 네임스페이스(namespace)를 제공한다. 아래 6단계를 통해 Address 클래스를 패키지화 시켜보자.


- 1단계 : 패키지 이름을 선택한다.
코드를 수정하기전, 먼저 패키지 이름을 무엇으로 할지 정해야 한다. Address 라는 클래스는 애초에 주소록이라는 애플리케이션의 일부로 포함시키기 위해 만들었었다. 그래서 Address 클래스에 addressbook 이라는 패키지 이름을 사용하기로 해보자.


- 2단계 : 디렉토리를 생성한다.
이번 단계에선 addressbook이라는 이름의 디렉토리를 생성해보자. 이 디렉토리에는 addressbook 패키지에 속하는 클래스들은 모두 저장되게 될 것이다. 이 디렉토리의 위치는 원하는 곳에 생성하면 된다. 


- 3단계 : 패키지 선언을 추가한다.
자, 이제 Address.fx 소스에 패키지 이름을 추가해보자. 패키지는 package라는 키워드 다음에 패키지 이름을 적게 되며, 소스의 제일 첫번째 줄에 넣어주면 된다.

package addressbook;

class Address {
     var street: String;
     var city: String;
     var state: String;
     var zip: String;
}


- 4단계 : 접근자를 추가한다.
이제 클래스에 접근자를 추가하자. 클래스를  public으로 선언하도록 해보자.

package addressbook;

public class Address {
     public var street: String;
     public var city: String;
     public var state: String;
     public var zip: String;
}

이 public 키워드에 대해서는 다음 레슨에서 배울것이며, 일단 다른 클래스에서의 모든 접근을 허용한다고만 알아두도록 하자.


- 5단계 : 소스를 컴파일한다.
addressbook 디렉토리로 이동하여 javafxc Address.fx 라고 입력하면 컴파일 된다. 컴파일이 완료되면 해당 디렉토리에 .class 파일들이 생성되었을 것이다.


- 6단계 : 클래스를 사용한다.
패키지화된 클래스를 사용하는 방식은 두가지 이다. 첫번째 방식은 "패키지명.클래스명" 과 같이 사용한다. 이름이 좀 길어진다는 단점이 있다. 두번째는 미리 import 라는 키워드를 통해 addressbook.Address 라는 "패키지명.클래스명"을 입력하여 해당 패키지의 클래스를 사용하겠다고 명시한후 클래스명인 Address 만으로 그 사용이 가능하다.

// Approach #1

addressbook.Address {
     street: "1 Main Street";
     city: "Santa Clara";
     state: "CA";
     zip: "95050";
}

// Approach #2
import addressbook.Address;

Address {
     street: "1 Main Street";
     city: "Santa Clara";
     state: "CA";
     zip: "95050";
}

이 예제를 packagetest.fx 라는 이름으로 저장하고 컴파일해보자. 별다른 에러없이 컴파일이 끝난다면 패키지를 잘 찾은 것이다.

반응형
<출처 : http://javaora.tistory.com/entry/Learning-the-JavaFX-Script-Programming-Language-Lesson-9-Writing-Your-Own-Classes >

목차
- Customer 예제
- 다른 클래스로부터의 상속

- Customer 예제
Lesson 2를 통해 객체(object)를 사용하는 방법에 대해 배웠었다. 그런데 먼저 .class 파일을 다운로드 받길 요청했고, 컴파일러를 가지고 Address와 Customer 객체를 생성하는 방법에 대해 알게 되었다. 아래 예제를 통해 빠뜨린 클래스의 정의 방법에 대해 알아보자.

def customer = Customer {
     firstName: "John";
     lastName: "Doe";
     phoneNum: "(408) 555-1212"
     address: Address {
          street: "1 Main Street";
          city: "Santa Clara";
          state: "CA";
          zip: "95050";
     }
}

customer.printName();
customer.printPhoneNum();
customer.printAddress();

class Address {
     var street: String;
     var city: String;
     var state: String;
     var zip: String;
}

class Customer {
     var firstName: String;
     var lastName: String;
     var phoneNum: String;
     var address: Address;

    function printName() {
        println("Name: {firstName} {lastName}");
    }

    function printPhoneNum(){
        println("Phone: {phoneNum}");
    }

    function printAddress(){
        println("Street: {address.street}");
        println("City: {address.city}");
        println("State: {address.state}");
        println("Zip: {address.zip}");
    }
}

위 예제는 변수와 함수에 대해 배울때 봐왔던 코드와 비슷하다. Address 클래스에는 street, city, state, zip 등의 인스턴스 변수들이 모두 String 타입으로 선언되어 있고, Customer 클래스에는 몇개의 인스턴스 변수와 그 값을 출력하는 변수들이 더해져 있다. 이 변수와 함수들은 클래스 내부에 선언되어져 있기 때문에 이들이 생성되는 곳 어디에서든 그 사용이 가능하다.


- 다른 클래스로부터의 상속

또한 클래스는 다른 클래스로부터 변수와 함수등을 상속받아 사용할 수 있다. 예를 들면, 은행에서 계좌에 저축하고 금액 확인하는 것은 생각해보자. 각 계좌는 계좌번호와 저축된 금액이 있다. 이 계좌에서 남은 금액을 조회하거나, 추가 예금을 하거나, 돈을 찾을 수도 있다. 이것을 변수와 함수를 가진 Account라는 클래스로 만들어 보자.

abstract class Account {

     var accountNum: Integer;
     var balance: Number;

     function getBalance(): Number {
          return balance;
     }

     function deposit(amount: Number): Void {
          balance += amount;
     }

     function withdraw(amount: Number): Void {
          balance -= amount;
     }
}

abstract 라는 키워드를 주목하자. 이 키워드의 의미는 Account 객체를 직접적으로 생성 할 수 없다는 뜻이다.(이렇게 생성한 목적은 오직 계좌에 저축을 하거나 저축금액을 확인하기 위함이다.)

accountNum 과 balance 라는 변수는 각각 계좌번호와 저축금액을 저장하게 된다. 남은 함수들은 각각 계좌에 남은 금액을 조회하거나(getBalance()), 저축을 하거나(deposit()), 출금을 하는(withdraw()) 기능만을 제공한다.

SavingsAccount 라는 클래스를 정의하자. 이 클래스는 extends 라는 키워드를 통해 변수와 함수를 상속 받는다.

class SavingsAccount extends Account {

     var minBalance = 100.00;
     var penalty = 5.00;

     function checkMinBalance() : Void {
          if(balance < minBalance){
               balance -= penalty;
          }
     }
}

SavingAccount 클래스는 Account의 서브 클래스로서 자동적으로 Account의 인스턴스 변수와 함수의 내용이 포함되며 사용 할 수 있다. 위 예에서 보듯 상속받는 클래스는 상속받는 변수와 함수외에 추가적으로 자신의 변수와 함수를 생성 할 수 있다.

Account 클래스를 상속받는 또 다른 클래스 CheckingAccount 라는 클래스를 정의해보자

class CheckingAccount extends Account {

     var hasOverDraftProtection: Boolean;

     override function withdraw(amount: Number) : Void {
          if(balance-amount<0 and hasOverDraftProtection){

               // code to borrow money from an overdraft account would go here

          } else {
               balance -= amount; // may result in negative account balance!
          }
     }
}

위 예제는 출금시 현재 저축액보다 더 많은 금액을 출금 할 수 없도록 막아 놓았다. 특이한 점은 withdraw라는 함수는 Account에 존재하는 함수로서 이를 재정의(override) 했다. 이렇게 재정의 되는 함수는 앞에 override라는 키워드를 사용하게 된다.
반응형
<출처 : 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