Java

[Java] 클래스간의 관계- 포함관계

gorusair 2022. 1. 19. 22:46

상속이외에도 클래스를 재사용하는 또  다른 방법이 있는데, 그것은 클래스간에 포함관계를 맺어

주는 것이다. 클래스 간의 포함관계를 맺어 주는 것은 한 클래스의 멤버변수로 다른 클래스 타입의 참조 변수를 선언하는 것을 뜻한다.

 

♠ 원을 표현하기 위한 Circle이라는 클래스를 작성

class Circle{
	int x;			//원점의 x 좌표
    int y; 			//원점의 y 좌표
    int r;			// 반지름
 }
 
 // 좌표상의 한 점을 다루기 위한 Point 클래스
 
 class Point{
 	int x; 		//x좌표
    int y; 		//y좌표
 }
 
 
 //Point 클래스를 재사용해서  Circle 클래스를 작성한다면 다음과 같이 할 수 있다.
 class Circle{
 	int x;
    int y;
    int r;
 }
 
 class Circle{
 	Point c = new Point();	//원점
    int r;
 }

하나의 거대한 클래스를 작성하는 것보다 단위별로 여러 개의 클래스를 작성한 다음, 이 단위 클래스들을 포함관계로 
재사용하면 보다 간결하고 손쉽게 클래스를 작성할 수 있다.

 

클래스간의 관계 결정하기

클래스를 작성하는데 있어서 상속관계를 맺어 줄 것인지 포함관계를 맺어 줄 것인지를 결정하는 것은 떄떄로 혼돈스러울 수있다.

위에서 예를 든 Circle 클래스의 경우, Point클래스를 포함시키는 대신 상속관계를 맺어 주었다면 다음과 같다.

class Circle P 
	point c = new Point();
    int r;
}


class Circle extends Point{
	int r;
}
원 은 점이다.   -  Circle is a Point
원 은 점을 가지고 있다 - Circle has a Point

클래스를 가지고 문장을 만들었을 떄 '~은 ~이다'라는 문장이 성립한다면, 서로 상속관계를 맺어주고,'~은 ~을 가지고 있다.'는 문장이 성립한다면 포함관계를 맺어주면 된다.

Circle클래스와 Point 클래스간의 관계는 상속관계보다 포함관계를 맺어주는것이 더 옳다.

 

상속관계 '~은 ~이다.(is-a)'

포함관계 '~은 ~을 가지고 있다.(has-a)'

 

public class DrawShape {
	public static void main(String[] args) {
		Point[] p = { 	new Point(100, 100),
					   	new Point(140, 50),
					   	new Point(200, 100)
		};
		
		Triangle t = new Triangle(p);
		Circle c = new Circle(new Point(150,150),50);
		
		t.draw();	//삼각형을 그린다.
		c.draw();	//원을 그린다.
	}
}

class Shape{
	String color = "black";
	void draw() {
		System.out.printf("[color=%s]%n", color);
	}
}

class Point{
	int x;
	int	y;
	
	Point(int x, int y){
		this.x = x;
		this.y	= y;		
	}
	Point(){
		this(0, 0);
	}
	String getXY() {
		return "("+x+","+y+")";	//x와 y의 값을 문자열로 반환
	}
}

class Circle extends Shape{
	Point center;	//원의  원점좌표
	int r;			//반지름
	
	Circle() {
		this(new Point(0,0),100);	//Circle(Point center,int r)를 호출
	}
	
	Circle(Point center, int r){
		this.center	= center;
		this.r	= r;
	}
	
	void draw() {	//원을 그리는 대신에 원의 정보를 출력하도록 했다.
		System.out.printf("[center=(%d,%d),r=%d, color=%s]%n",
						center.x, center.y, r, color);
	}
}

class Triangle extends Shape{
	Point[] p =new Point[3];
	
	Triangle(Point[] p) {
		this.p = p;
	}
	void draw() {
		System.out.printf("[p1=%s, p2=%s,p3=%s, color=%s]%n",
			p[0].getXY(), p[1].getXY(), p[2].getXY(),color);
	}
}
[p1=(100,100), p2=(140,50),p3=(200,100), color=black]
[center=(150,150),r=50, color=black]
public class DeckTest {
	public static void main(String[] args) {
		Deck d = new 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;
		}
	}
}	// Deck 클래스 끝

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"; // 숫자 10은 X로 표현
		
		return "kind : " + kinds[this.kind]
				+", number : " + numbers.charAt(this.number);
	}
}

 

단일 상속

다른 객체지향 언어인 C++에서는 여러 조상 클래스로부터 상속받는 것이 가능한 '다중상속'을 허용하지만 자바에서는 오직 단일 상속만을 허용한다. 그래서 둘 이상의 클래스로 부터 상속을 받을 수 없다.

다중상속에 비해 클래스 간의 관계가 보다 명확해지고 코드를 더욱 신뢰할  수 있게 만들어 준다는 점에서 이점을 가지는 단일상속이다.

 

package ch07;

class TV{
	boolean power;
	int channel;
	
	void power()	{power = !power;}
	void channelUp() {++channel;}
	void channelDown() {--channel;}
}
class VCR{
	boolean power;
	int counter = 0;
	void power() {	power =!power;}
	void play()	{/*내용생략*/}
	void stop()	{/*내용생략*/}
	void rew()	{/*내용생략*/}
	void ff()	{/*내용생략*/}
}
public class TVCR extends Tv {
	
	VCR vcr = new VCR();
	
	void play() {
		vcr.play();
	}
	void stop() {
		vcr.stop();
	}
	void rew() {
		vcr.rew();
	}
	void ff() {
		vcr.ff();
	}

}

Object 클래스 - 모든 클래스의 조상

object 클래스는 모든 클래스 상속계층도의 최상위에 있는 조상클래스이다. 다른 클래스로 부터 상속 받지 않는 모든 클래스들은 자동적으로 Object 클래스로부터 상속받게 함으로 이것을 가능하게 한다.