Java/Java Study

[객체지향] 다형성, Polymorphism - Part3 (다형성의 장점)

모모토 2021. 5. 13. 18:54
반응형

● 다형성 part 1, 2에서 배운 다형성을 간략하게 정리해보자,

 

part - 1) 조상타입의 참조변수로 자손타입의 객체를 생성할 수 있다.(자손의 인스턴스를 참조할 수 있다. 단, 조상타입의 참조변수로 자손타입의 멤버변수를 호출할 수는 없다.)

 

part - 2) 상속관계의 두 클래스는 서로 참조변수의 형 변환이 가능하다. (다만 주의해야 할 점은 조상타입을 자손타입 다운 캐스팅할 때 조상타입의 참조변수가 자신의 인스턴스를 가리키고 있다면 이는 part - 1에 위배됨으로 형 변환하려는 참조변수가 어떤 인스턴스를 참조하고 있는지를 파악해야 한다.)

 

자 그러면 이런 다형성의 개념이 어떤 장점 때문에 쓰이는지를 알아보자.

 

1) 매개변수에 다형성을 줄 수 있다.

 

매개변수에 다형성을 준다는 게 무슨 의미일까?

 

매개변수의 다형성을 설명하기 전에 자바 코드로 고객과 고객이 구매하는 물품을 구현해보자

 

class Customer {
	int money = 1000;
	
	void Purchase (Tv x) {
		if(money < x.price) {
			System.out.println("금액이 부족해 구매할 수 없습니다. 모자란 금액"+(x.price - money));
			return;
		}else {
		money = money - x.price;
		System.out.println("Tv를 구매하였습니다. 남은돈"+money);
		}
	}
	
	void Purchase (Computer x) {
		if(money < x.price) {
			System.out.println("금액이 부족해 구매할 수 없습니다. 모자란 금액"+(x.price - money));
			return;
		}else {
		money = money - x.price;
		System.out.println("Computer를 구매하였습니다. 남은돈"+money);
		}
	}
	void Purchase (Audio x) {
		if(money < x.price) {
			System.out.println("금액이 부족해 구매할 수 없습니다. 모자란 금액"+(x.price - money));
			return;
		}else {
		money = money - x.price;
		System.out.println("Audio를 구매하였습니다. 남은돈"+money);
		}
	}
	void Purchase (Car x) {
		if(money < x.price) {
			System.out.println("금액이 부족해 구매할 수 없습니다. 모자란 금액"+(x.price - money));
			return;
		}else {
		money = money - x.price;
		System.out.println("Car를 구매하였습니다. 남은돈"+money);
		}
	}
}

class Tv {
	int price = 300;
}

class Computer {
	int price = 400;
}

class Audio {
	int price = 100;
}

class Car {
	int price = 500;
}


public class PolyArgument_Practice {
	public static void main(String[] args) {
		Customer cus = new Customer();
		Tv tv = new Tv();
		Computer com = new Computer();
		Audio audio = new Audio();
		Car car = new Car();
		cus.Purchase(com);
		cus.Purchase(tv);
		cus.Purchase(audio);
		cus.Purchase(car);
	}
}

 

Customer 클래스로 물품을 구매하기 위해선, 구매할 물건의 참조변수를 받는 메서드를 일일이 만들어줘야 한다 (오버로딩을 물건 클래스의 개수만큼 해주어야한다.) 매장에 물건이 생길때마다 오버로딩을 해줘야 하고 코드가 필요 이상으로 길어진다. 여기서 매개변수의 다형성을 이용해 주자. Tv, Audio, Computer, Car의 공통조상을 만들면 조상타입으로 자손타입 인스턴스를 참조할 수 있으므로 오버로딩할 필요 없이 하나의 메서드로 모든 물건을 구매할 수 있게 된다. 

 

그렇다면 공통조상 class를 만들어서 위의 코드를 간략하게 해 보자.

 

class Product { //조상클래스
	int price;
	
	Product (int price) {
		this.price=price;
	}
}

class Customer {
	int money = 1000;
	
	void Purchase (Product x) { //Product 타입의 참조변수를 매개변수로 한다.(Product를 상속한)
		if(money < x.price) {
			System.out.println("금액이 부족해 구매할 수 없습니다. 모자란 금액"+(x.price - money));
			return;
		}else {
		money = money - x.price;
		System.out.println(x.toString()+"를 구매하였습니다. 남은돈"+money);
		}
	}
}

class Tv extends Product {
	Tv(){
		super(300);
	}
	public String toString() {
    //Object 클래스와 상속관계므로 toString을 오버라이딩
    //접근제어자는 public 해주어야한다. 오버라이딩 규칙
		return "TV";
	}
}

class Computer extends Product {
	Computer(){
		super(400);
	}
	public String toString() { 
		return "Computer";
	}
}

class Audio extends Product {
	Audio(){
		super(100);
	}
	public String toString() {
		return "Audio";
	}
}

class Car extends Product {
	Car(){
		super(500);
	}
	public String toString() {
		return "Car";
	}
}


public class PolyArgument_Practice {
	public static void main(String[] args) {
		Customer cus = new Customer();
		Tv tv = new Tv();
		Computer com = new Computer();
		Audio audio = new Audio();
		Car car = new Car();
		cus.Purchase(com);
		cus.Purchase(tv);
		cus.Purchase(audio);
		cus.Purchase(car);
	}
}

 

위처럼 코드를 간략하게 짤 수 있고 , 무엇보다도 다른 물건을 추가하여도 바꿔야 할 코드(오버로딩 추가할 필요)가 없다는 게 장점이며 그로 인하여 더욱 편하게 코드를 수정할 수 있게 되었다.

 

실행결과

Computer를 구매하였습니다. 남은돈600
TV를 구매하였습니다. 남은돈300
Audio를 구매하였습니다. 남은돈200
금액이 부족해 구매할 수 없습니다. 모자란 금액300
구매한 물품들
Computer
TV
Audio

 


2) 여러 종류의 객체를 배열로 다룰 수가 있다.

 

다형성에 의해서 Product p1 = new Tv();처럼 객체를 생성하는 게 가능하다. Audio, Car, Computer도 마찬가지이다.

 

그렇다면 Product 타입의 배열을 만들어 인덱스마다 Tv, Audio, Car, Computer의 인스턴스를 넣어줄 수가 있게 된다. 같은 조상타입을 상속받는다는 이유로 배열 속에 각기 다른 타입의 인스턴스들을 저장할 수 있다는 것이 바로 다형성의 장점이다.

 

Product[] item = new Product[10];

item[0] = new Tv();
item[1] = new Computer();
item[2] = new Audio();
item[3] = new Car();

 

배열로 다룰 때의 단점인 물품의 개수가 몇 개인지 모르는 상황에서 만약 물품의 개수가 10이 넘어가면 코드를 또 일일이 수정해야 하기에 가변배열을 사용하자

 

Vector item = new Vector();

item.add(new Tv());
item.add(new Car());
item.add(new Computer());
item.add(new Audio());

 

벡터를 이용하여 짠 코드 ( List 메서드를 추가해 내가 구매한 물품의 리스트를 출력하기 위해 Vector 사용)

 

import java.util.Vector;

class Product {
	int price;
	
	Product (int price) {
		this.price=price;
	}
}

class Customer {
	int money = 1000;
	Vector item = new Vector();
	int i=0;
	void Purchase (Product x) {
		if(money < x.price) {
			System.out.println("금액이 부족해 구매할 수 없습니다. 모자란 금액"+(x.price - money));
			return;
		}
		
		money = money - x.price;
		item.add(x);
		System.out.println(x.toString()+"를 구매하였습니다. 남은돈"+money);

	}
	void List () {
		String itemList="";
		if(item.isEmpty()) {
			System.out.println("구입하신 물품이 없습니다.");
			return;
		}
		
		for(int i=0; i<item.size();i++) {
			
			Product p =(Product)item.get(i);
			itemList += (i==0) ? ""+ p : ", "+p;
			
		}System.out.println("구매한 물품들");
		 System.out.println("---------");
		 System.out.println(itemList);
	}
}

class Tv extends Product {
	Tv(){
		super(300);
	}
	 public String toString() { 
		return "TV";
	}
}

class Computer extends Product {
	Computer(){
		super(400);
	}
	public String toString() {
		return "Computer";
	}
}

class Audio extends Product {
	Audio(){
		super(100);
	}
	public String toString() {
		return "Audio";
	}
}

class Car extends Product {
	Car(){
		super(500);
	}
	public String toString() {
		return "Car";
	}
}


public class PolyArgument_Practice {
	public static void main(String[] args) {
		Customer cus = new Customer();
		Tv tv = new Tv();
		Computer com = new Computer();
		Audio audio = new Audio();
		Car car = new Car();
		cus.Purchase(com);
		cus.Purchase(tv);
		cus.Purchase(audio);
		cus.Purchase(car);
		cus.List();
	}
}

 

실행결과

 

Computer를 구매하였습니다. 남은돈600
TV를 구매하였습니다. 남은돈300
Audio를 구매하였습니다. 남은돈200
금액이 부족해 구매할 수 없습니다. 모자란 금액300
구매한 물품들
---------
Computer, TV, Audio