[기본 개념] 2 | (1.2) 클래스 간 관계
1 상속의 정의와 장점
2> 클래스간의 관계 - 포함관계
3> 클래스간의 관계 결정하기
4 단일상속(single inheritance)
5 Object클래스 - 모든 클래스의 조상
2. 클래스간의 관계 - 포함관계
상속이외에도 클래스를 재사용하는 또 다른 방법은 클래스간에 '포함(Composite)'관계를 맺어주는 것이다. 이는 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것을 뜻한다.
원(Circle)을 표현하기 위한 Circle이라는 클래스를 다음과 같이 작성하였다고 가정하자.
class Circle {
int x ; // 원점의 x좌표
int y ; // 원점의 y좌표
int r ; // 반지름 (radius)
}
그리고 좌표상의 한 점을 다루기 위한 Point클래스가 다음과 같이 작성되어 있다고 하자.
class Point {
int x ; // x좌표
int y ; // y좌표
}
Point클래스를 재사용해서 Circle클래스를 작성한다면 다음과 같이 할 수 있을 것이다.
class Circle { int x ; // 원점의 x좌표 int y ; // 원점의 y좌표 int r ; // 반지름 (radius) } |
----> | class Circle { Point c = new Point( ) : // 원점 int r ; } |
포함관계를 맺어주면, 클래스를 작성하는 것도 쉽고 코드도 간결해서 이해하기 쉽다. 그리고 단위클래스별로 코드가 작게 나뉘어 작성되어 있기 때문에 코드도 관리하는데 수월하다.
1.3 클래스간의 관계 결정하기
상속관계를 맺어 줄 건지 포함관계를 맺어 줄건지 결정하는 것이 혼란스러울 수 있다. Circle클래스와 Point 클래스의 경우 '~은 ~이다(is-a)'와 '~은 ~을 가지고 있다(has-a)'를 넣어서 문장을 만들어 보면 관계가 명확해진다.
원(Circle)은 점(Point)이다. - Circle is a Point.
원(Circle)은 점(Point)을 가지고 있다. - Circle has a Point.
원은 원점(Point)과 반지름으로 구성되므로 두 번째 문장이 더 옳다는 것을 알 수 있다.
이처럼 '~은 ~이다.'라는 문장이 성립하면 상속관계를 맺어 주고, '~은 ~을 가지고 있다.'라는 문장이 성립한다면 포함관계를 맺어주는 것이 더 옳다.
상속관계 '~은 ~이다.(is-a)'
포함관계 '~은 ~을 가지고 있다.(has-a)'
예를 들면, Card클래스와 Deck클래스는 'Deck은 Card를 가지고 있다.'라는 문장이 더 옳기 때문에 Deck클래스에 Card클래스를 포함시켜야 한다.
예제/DeckTest.java
public class DeckTest {
public static void main(String[] args) {
Deck d = new Deck(); // 카드 한벌(Deck)을 만든다.
Card c = d.pick(0); // 섞기 전에 제일 위의 카드를 뽑는다.
System.out.println(c); // System.out.println(c.toString());과 같다.
d.shuffle(); // 카드를 섞는다.
c = d.pick(0); // 섞은 후에 제일 위의 카드를 뽑는다.
System.out.println(c);
}
}
class Deck {
final int CARD_NUM = 52; // 카드의 개수
Card cardArr[] = new Card[CARD_NUM]; // Card객체 배열을 포함
Deck() { // Deck의 카드를 초기화한다.
int i = 0;
for(int k = Card.KIND_MAX; k > 0; k--)
for(int n = 0; n < Card.NUM_MAX; n++)
cardArr[i++] = new Card(k, n+1);
}
Card pick(int index) { // 지정된 위치(index)에 있는 카드 하나를 꺼내서 반환
return cardArr[index];
}
Card pick() { // Deck에서 카드 하나를 선택한다.
int index = (int)(Math.random() * CARD_NUM);
return pick(index);
}
void shuffle() { // 카드의 순서를 섞는다.
for(int i = 0; i < cardArr.length; i++) {
int r = (int) (Math.random() * CARD_NUM);
Card temp = cardArr[i];
cardArr[i] = cardArr[r];
cardArr[r] = temp;
}
}
}
class Card {
static final int KIND_MAX = 4; // 카드 무늬의 수
static final int NUM_MAX = 13; // 무늬별 카드 수
static final int SPADE = 4;
static final int DIAMOND = 3;
static final int HEART = 2;
static final int CLOVER = 1;
int kind;
int number;
Card() {
this(SPADE, 1);
}
Card(int kind, int number) {
this.kind = kind;
this.number = number;
}
public String toString() {
String[] kinds = {"", "CLOVER", "HEART", "DIAMOND", "SPADE"};
String numbers = "0123456789XJQK";
return "kind : " + kinds[this.kind]
+ ", number : " + numbers.charAt(this.number);
}
}
실행결과
kind : SPADE, number : 1
kind : SPADE, number : 9
Deck클래스를 작성하는데 Card클래스를 재사용하여 포함관계로 작성하였다. 카드 한벌은 모두 52장의 카드로 이루어져 있으므로 Card클래스를 크기가 52인 배열로 처리하였다. shuffle( )은 카드 한 벌의 첫 번째 카드부터 순서대로 임의로 위치에 있는 카드와 위치를 서로 바꾸는 방식으로 카드를 섞는다. random( )을 사용했기 때문에 매 실행 시마다 결과가 다르게 나타날 것이다.
아래의 문장에서 pick(0)을 호출하면, 매개변수 index의 값이 0이 되므로, cardArr[0]에 저장된 Card객체의 주소(객체가 아니라 객체의 주소)가 참조변수 c에 저장된다.
Card c = d.pick(0) ; // pick(int index)를 호출
Card pick(int index) {
return cardArr[index] ;
}
Card클래스에 정의된 toString( )은 인스턴스의 정보를 문자열로 반환할 목적으로 정의된 것이다. 이처럼 참조변수의 출력이나 덧셈연산자를 이용한 참조변수와 문자열의 결합에는 toStrinf( )이 자동적으로 호출되어 참조변수를 문자열로 대치한 후 처리한다.
출처 | Java의 정석 (남궁 성)
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[기본 개념] 2 | (2.1) 오버라이딩, Super( ) (0) | 2021.11.18 |
---|---|
[기본 개념] 2 | (1.3) 단일상속, Object 클래스 (0) | 2021.11.18 |
[기본 개념] 2 | (1.1) 상속 (0) | 2021.11.18 |
[기본 개념] 1 | (5.1) 변수의 초기화 (0) | 2021.09.01 |
[기본 개념] 1 | (4.1) 생성자 (0) | 2021.08.25 |