본문 바로가기

Language/Java

[Java] 다형성 활용하기

앞에서 공부한 상속과 다형성을 활용하면 프로그램을 유지 보수하는데 매우 편리하다. 

이때 배열을 함께 사용하면 여러 하위 클래스 자료형을 상위 클래스 자료형으로 한꺼번에 관리할 수도 있다.

 

일반 고객과 VIP 고객의 중간 등급 만들기

예제 시나리오

고객이 늘어 VIP 고객만큼 물건을 많이 구매하지는 않지만, 그래도 단골인 분들에게 혜택을 주고 싶다. 그래서 GOLD 고객 등급을 하나 추가한다. GOLD 고객 혜택은 다음과 같다.

● 제품을 살 때는 항상 10% 할인해 준다.
● 보너스 포인트를 2% 적립해 준다.
● 담당 전문 상담원은 없다.

 

[실습] 새로운 고객 등급 추가하기
package polymorphism;

public class GoldCustomer extends Customer {
	double saleRatio;
	
	public GoldCustomer(int customerID, String customerName) {
		super(customerID, customerName);
		customerGrade = "GOLD";
		bonusRatio = 0.02;
		saleRatio = 0.1;
	}
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price = (int)(price - saleRatio);
	}
}

 

GOLDCustomer 클래스는 지불 가격과 보너스 포인트를 계산하는 calcPrice() 메서드만 재정의했다. 

이처럼 상속을 사용하면 새로운 기능이 추가되더라도 쉽게 구현할 수 있다.

 

 

배열로 고객 5명 구현하기

예제 시나리오

이 회사의 고객은 현재 5명이다. 5명 중 VIP 1명, GOLD 2명, SILVER 2명이다.
이 고객들이 각각 10,000원짜리 상품을 구매했을 때 결과를 출력한다.

 

객체 배열 ArrayList는 자료형을 지정하여 선언해야 한다. 

사용할 클래스는 Customer, GoldCustomer, VIPCustomer 세 종류이다.

VIPCustomer 클래스와 GOLDCustomer 클래스 모두 Customer에서 상속받은 클래스이므로 배열의 자료형을 Customer로 지정하면 모두 사용이 가능하다. 

그리고 이 배열에 Customer 하위 클래스의 인스턴스가 추가될 때 모두 Customer형으로 묵시적 형 변환이 된다.

ArrayList<Customer> customerList = new ArrayList<Customer>();

 

[실습] 배열을 활용한 고객 관리 프로그램 구현하기
package polymorphism;
import java.util.ArrayList;

public class CustomerTest1 {
	public static void main(String[] args) {
		ArrayList<Customer> customerList = new ArrayList<Customer>();
		
		Customer customerLee = new Customer(10010, "이순신");
		Customer customerShin = new Customer(10020, "신사임당");
		Customer customerHong = new GoldCustomer(10030, "홍길동");
		Customer customerYoul = new GoldCustomer(10040, "이율곡");
		Customer customerKim = new VIPCustomer(10050, "김유신", 12345);
		
		customerList.add(customerLee);
		customerList.add(customerShin);
		customerList.add(customerHong);
		customerList.add(customerYoul);
		customerList.add(customerKim);
		
		System.out.println("==== 고객 정보 출력 ====");
		for(Customer customer : customerList) {
			System.out.println(customer.showCustomerInfo());
		}
		
		System.out.println("==== 할인율과 보너스 포인트 계산 ====");
		int price = 10000;
		for(Customer customer : customerList) {
			int cost = customer.calcPrice(price);
			System.out.println(customer.getCustomerName() + " 님이 " + cost +
					"원 지불하셨습니다.");
			System.out.println(customer.getCustomerName() + " 님의 현재 보너스 포인트는 "
					+ customer.bonusPoint + "점입니다.");
		}
	}
}

 

객체 배열 ArrayList를 선언하여 Customer, VIPCustomer, GoldCustomer 클래스의 인스턴스를 ArrayList에 추가했다.

향상된 for문을 사용하여 고객정보와 각 고객이 지불한 금액, 적립된 보너스 포인트를 출력한다.

for(Customer customer : customerList) 문장은 customerList 배열의 요소를 하나씩 가져와 Customer형 변수에 넣는다.

고객 정보를 ArrayList 배열에 저장할 때 Customer형으로 형 변환하여 저장했기 때문에 배열 요소를 가져올 때 Customer형으로 가져오게 된다.

각 인스턴스가 calcPrice() 메서드를 호출하면 현재 변수의 실제 인스턴스에 따라 재정의한 메서드를 각각 호출하여 계산한다.

이러한 것들이 다형성이다.

 

 

만약 상속이 사용하지 않고 Customer 클래스에 VIP와 GOLD 고객등급을 추가한다면

if(customerGrade == "VIP") {  // 할인 해주고, 적립 많이 해주고
}
else if(customerGrade == "GOLD") {  // 할인 해주고, 적립 적당히 
}
else if(customerGrade == "SILVER") {  // 적립만 해줌
}

고객 등급에 따라 다르게 구현해야 하기 때문에 if-else if 문을 사용한다.

이렇게 구현되는 경우 고객의 등급이 하나라도 추가되거나 삭제되면 유지보수가 복잡해진다.

그래서 상속을 사용해서 공통부분은 상위 클래스에, 각 등급별 내용은 하위 클래스에 구현하게 되면 새로운 등급의 고객이 추가되더라도 기존의 코드를 거의 수정하지 않고 새로운 클래스를 추가할 수 있다. 또한 프로그램이 확장성 있고 유지 보수하기 좋다.

 

 

상속은 항상 사용하는 것보다 IS-A 관계(is a relationship; inheritance)에서 사용하는 것이 가장 효율적이다.

IS-A 관계란 '사람은 포유류이다'와 같이 일반적인 개념과 구체적인 개념의 관계를 말한다. 

일반 클래스를 점차 구체화하는 상황에서 상속을 사용하는 것이다.

상속을 사용하면 많은 장점이 있지만 하위 클래스가 상위 클래스형에 종속되기 때문에 이질적인 클래스 간에 상속을 사용하지 않는 것이 좋다.

 

package inheritance2;

public class Subject {
	private int subjectID;
	private String subjectName;
	
	public int getSubjectID() {
		return subjectID;
	}
	
	public void setSubjectID(int subjectID) {
		this.subjectID = subjectID;
	}
	
	public String getSubjectName() {
		return subjectName;
	}
	
	public void setSubejctName(String subjectName) {
		this.subjectName = subjectName;
	}
	
	public void showSubjectInfo() {
		System.out.println(subjectID + ", " + subjectName);
	}
}

위와 같이 Subject 클래스를 구현했고, 여기에 Student(학생) 클래스를 만들고자 한다.

모든 학생들은 전공과목(Subject)을 가지고 있다.

class Student extends Subject {}

이런 경우 위와 같이 Subject 클래스를 상속받아 Student 클래스를 만드는 것이 좋을까?

Subject가 Student를 포괄하는 개념의 클래스가 아니기 때문에 답은 '아니다'이다.

이런 경우 'HAS-A 관계(has a relationship; association)'로 표현한다.

HAS-A 관계란 한 클래스가 다른 클래스를 소유한 관계를 말한다.

Subject는 Student에 포함되어 Student의 멤버 변수로 사용하는 것이 적절하다.

class Student {
	Subject majorSubject;
}

 

'Language > Java' 카테고리의 다른 글

[Java] 추상 클래스  (0) 2022.12.21
[Java] 다운 캐스팅과 instanceof  (0) 2022.12.20
[Java] 다형성(polymorphism)  (0) 2022.12.15
[Java] 메서드 오버라이딩  (1) 2022.12.13
[Java] 상속에서 클래스 생성과 형 변환  (0) 2022.11.20