티스토리 뷰

자바에서 변수는 객체 타입을 가지는 경우 객체에 대한 참조를 가진다. 객체의 필드의 경우에도 마찬가지여서 필드가 다른 객체 타입을 가질 때는 객체 안에 그 참조를 가지는 메모리가 잡힌다. 즉 그 객체 안에는 참조만 들어있고 참조가 다른 곳에 있는 객체를 가리킨다. 이러한 객체들은 모두 new 되어 힙메모리 어딘가에 자리잡고 있을 것이다. 다음 그림은 학생 클래스의 객체 예를 보여준다. 학생이 학번, 이름, 학년, 학과, 점수배열을 가지고 있다고 해 보자. 

여기서 int 타입의 학번과 학년은 객체 안에 해당 필드가 값을 가진다. (값 필드) 그러나 String이나 배열 타입의 객체 필드는 참조만 가진다. 이 참조는 어딘가에 있는 스트링 객체나 배열 객체를 가리킬 것이다. 처음에 객체가 new로 생성되었을 때는 객체의 모든 필드가 0으로 리셋되므로 이 참조들은 모두 null을 가지고 있다. 객체가 생성자나 다른 메소드에서 이런 필드에 참조를 지정하면(객체를 생성하거나 다른 데서 가져온 객체를 가리킴) 이제 객체는 다른 객체를 가리키는 필드를 가지게 된다.

여기서 스트링과 배열의 참조가 아니라 다른 클래스 객체의 참조를 가지는 경우를 생각해 보자. 그리고 그 객체가 뭔가 일을 하는데, 이 다른 객체의 정보가 필요한 경우가 있을 것이다. 예를 들어 앞의 해시맵 예에서 학생이 팀에 속하고 팀은 소속 학생의 리스트를 가지는 예가 있다. 그리고 학생도 자기가 속한 팀을 가리키는 참조를 가질 수 있다. 아래 그림과 같이 서로 참조하는 복잡한 관계를 형성하게 될 것이다.

여기서 학생이 팀을 필드로 가지고 팀은 학생의 배열을 필드로 가진다. 프로그램은 먼저 이러한 관계를 만족하게 데이터를 읽을 때 자료구조를 완성하는 것이 필요하다. 해시맵 예에서는 한 가지가 더 개입되어서 Main 클래스가 번호에 해당하는 학생을 찾아주어야 한다. 다음은 팀이 멤버를 읽어 자신의 memberList에 추가하는 코드다.

n = scan.nextInt();
Student st = Main.GetInstance().getStudent(n);
memberList.add(st);
st.setTeam(this);

여기서 마지막 줄이 학생에게 팀을 설정해 주는 메소드를 호출한다. 즉 여기서 팀에 소속할 학생들을 찾아 리스트에 넣으면서 팀이 만들어지는데, 학생에게 너의 팀은 나(this)다 라고 알려주는 것이다. 그러면 학생도 팀을 가지고 팀도 학생을 가지게 된다. 예를 들어 학생이 점수를 얻어서 그 점수를 팀에게도 더해주는 경우가 있다고 가정해 보자. 학생 코드에서 바로 그 학생의 소속팀 참조를 가져올 수 있으므로 팀의 점수를 추가하는 메소드를 호출한다.

void addScore(int n) {  // 학생이 점수를 취득하였을 때 불려지는 메소드
    myScore += n;       // 학생의 점수에 더하고
    myTeam.addScore(n); // 소속 팀의 점수에도 더한다
}

이렇게 팀의 필드를 가지고 있으면 바로 팀에 메소드를 불러서 작업을 하게 할 수 있다. 자바 객체는 다른 객체를 가지고 있는 경우가 매우 많다. 이것은 키값이나 번호, 이름 등을 가지는 것보다 훨씬 효율적이다. 예를 들어 팀이 학생의 번호만 가지고 있다면 학생 정보를 필요로 할 때마다 번호로 학생을 찾아야 할 것이다. 마찬가지로 팀과 학생의 관계에서도 학생이 자기가 속한 팀 이름을 가진다면 매번 그 이름의 팀을 찾거나 팀의 이름과 학생의 이름이 같은가 비교해야 할 것이다. 참조는 4바이트만 차지하고 그 참조로 다른 객체의 모든 필드와 메소드를 바로 접근할 수 있으므로 항상 참조를 가지고 있는 것이 가장 좋은 방법이다. 데이타베이스는 다른 객체를 가리킬 때 그 키값을 가지지만 프로그램에서 메모리에 생성된 객체는 참조를 가진다 라고 생각해 볼 수 있다.

프로그램은 객체들 간의 관계에 의해 여러 가지 동작을 하게 된다. 예를 들어 팀에 속한 학생들에게 메시지를 전달하고 싶다면 다음과 같은 팀 메소드를 만들 수 있다.

for (Student st : memberList)     st.sendMessage(msg);

또한 둘 이상의 팀에 속하거나 어느 팀에도 소속되지 않은 학생을 찾아야 한다면? 우리는 학생의 setTeam에서 이미 팀이 설정되어 있는 경우 둘 이상의 팀에 속하는 것으로 판별할 수 있다. 또한 팀이 모두 입력된 후 myTeam 필드가 아직도 null인 학생은 소속된 팀이 없는 학생이다. 또는 어떤 학생이 속한 팀의 팀장에게 연락을 해야 한다면? 팀이 팀장에 해당하는 학생의 필드를 가지고 있을 것이다. 그러므로 학생의 팀의 팀장 이렇게 참조를 몇번 타고 가면 팀장의 메소드를 부를 수 있다.

그럼 여기서 클래스와 클래스의 관계를 한번 생각해 보자. 클래스는 필드로 다른 클래스의 객체를 가리킨다. 위의 학생과 팀의 예제를 생각해 보면 한 학생은 하나의 팀에 속하고 팀은 여러 학생을 가진다. 그러므로 학생과 팀의 관계는 n:1의 관계여서 학생 클래스는 팀 필드를 가지고 팀은 학생의 리스트를 필드로 가진다. 또한 팀에는 한 명의 팀장이 있는 경우도 생각해 보자.  팀과 팀장은 1:1의 관계이므로 팀은 팀장을 필드로 가진다. 이와 같이 클래스 간의 관계가 어떤 형태냐에 따라 자료구조가 달라진다. 여러 개의 객체를 가지는 경우 그것을 리스트로 가질지, 배열로 가질지 또는 인덱스를 가지는 맵으로 가질지는 문제에 따라 결정해야 한다. 그러나 먼저 정해져야 할 것은 하나만 필드로 가지면 되는지, 아니면 두 개 이상 여러 개를 가져야 하는지를 결정하는 것이다. 

한편 멀티:멀티의 관계도 있다. 예를 들어 학생이 도서관에서 책을 대출하는 경우를 생각해 보자. 한 학생은 여러 권의 책을 빌릴 수 있고 책의 입장에서는 여러 권이 있으므로 빌려간 학생이 여러 명이 될 수 있다. 이런 관계는 보통 서로 리스트를 가지는 것은 바람직하지 않다. 대출이나 반납에서 두 개의 리스트를 수정해야 하므로 (검색, 비교, 수정) 비용이 많이 든다. 차라리 학생이 빌린 책의 리스트만 가지고 있고 책에 대해 그 책을 빌려간 학생을 모두 찾아야 한다면 그것은 그 때 가서 학생에게 모두 물어봐서 검색을 하는 방법을 취할 수 있다. (물론 그런 일이 자주 생긴다면 따로 자료구조를 만들어두는 것이 좋다.) 이런 객체간의 관계를 어떻게 클래스로 표현할 것인가가 클래스 설계의 핵심이다. 데이터베이스에서 엔티티 관계 다이어그램을 그리는 것과 비슷하게 이런 클래스 간의 관계도 다이어그램으로 나타내진다.

다음 그림은 학생과 책과 도서관의 관계를 나타내고 있다. 여기서는 학생이 자신이 대출한 책의 리스트를 가지고 있다. 책의 입장에서는 자신을 대출해간 학생의 리스트를 갖지 않는다. (그건 필요하면 그 때 가서 검색해서 계산해야 한다.)

또 다른 측면으로 앞에서 살펴본 싱글톤도 이러한 클래스/객체 간의 관계를 나타내는 한 예다. 클래스의 객체가 항상 하나만 존재하므로 우리는 따로 그 참조를 가지고 있을 필요가 없고 클래스에게 너의 객체를 달라고 요청할 수 있다. 클래스는 자신의 유일한 객체의 관리를 직접 하고 내부적으로 정적 필드를 가지고 있게 된다. 위의 예에서 학생과 책의 리스트를 가지고 전체적인 프로그램을 동작시키는 도서관 클래스가 있다면 도서관 클래스는 하나의 객체만 존재해야 되고 그러므로 싱글톤 패턴을 따르는 것이 바람직하다. 많은 경우 메인 클래스 객체는 하나의 인스턴스만 가지는 경우가 많다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함