모든지 기록하자!

[Java] 다형성 ( Polymorphism) (예제: 고객관리) 본문

Java

[Java] 다형성 ( Polymorphism) (예제: 고객관리)

홍크 2021. 5. 17. 22:45
728x90

다형성이란? 

다형성이란 하나의 코드가 여러 자료형으로 구현되어 실행되는 것을 말한다.

3개의 클래스가 Animal 클래스를 상속받는 경우를 보자

Animal 클래스에 메서드를 하나 정의하고 상속받은 클래스에서 재정의한다.

class Animal{
	public void move() {
		System.out.println("동물이 움직입니다.");
	}
}
class Human extends Animal{
	public void move() {
		System.out.println("사람이 두발로 걷습니다.");
	}	
}
class Tiger extends Animal{
	public void move() {
		System.out.println("호랑이가 네발로 뜁니다.");
	}
}
class Eagle extends Animal{
	public void move() {
		System.out.println("독수리가 하늘을 날읍니다.");
	}	
}
public class AnimalTest {

	public static void main(String[] args) {
	
		AnimalTest test = new AnimalTest();
		test.moveAnimal(new Human());
		test.moveAnimal(new Tiger());
		test.moveAnimal(new Eagle());
		
        //Animal animal = new Human(); 동일한 코드
	}
	public void moveAnimal(Animal animal) {
			
		animal.move();	
}

실행결과

animal.move();

한 줄의 코드로 3가지 출력문이 실행된다. 이 한 줄의 코드가 다형성을 나타내고 있는 것이다.

 

다형성을 활용해 VIP 고객 클래스 완성하기

public class Customer {
	
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	int bonusPoint;
	protected double bonusRatio;
	
	public Customer() {
		initCustomer(); // 고객 등급과 보너스 포인트 적립률 지정함수
	}
	
	public Customer(int customerID, String customerName){
		this.customerID = customerID;
		this.customerName = customerName;
		initCustomer();
	}
    private void initCustomer(){
    	customerGrade = "SILVER";  // 멤버 변수의 초기화 부분
        bonusRatio = 0.01;
    }
	
	public int calcPrice(int price){
		
		bonusPoint += price * bonusRatio;		
		return price;
	}
	
	public String showCustomerInfo(){
		return customerName + "님의 등급은 " + customerGrade + "이고, 보너스 포인트는 
        " + bonusPoint + "점 입니다.";  
	}
    ...(생락)

기존 Customer 클래스와 달라진 점은 initCustomer() 메서드가 있다.

이 메서드는 클래스의 멤버 변수를 초기화하는데 Customer 클래스를 생성하는 두 생성자에서

공통으로 사용하기 때문에 메서드로 만들었다.

 

VIP 고객 클래스 코드를 수정해보자

public class VIPCustomer extends Customer{
	
	private int agentID;
	private double saleRatio;
	
	public VIPCustomer(int customerID, String customerName, int agentID) {
		
		super(customerID,customerName);
		customerGrade = "VIP";
		bonusRatio = 0.05;
		saleRatio = 0.1;
		this.agentID = agentID;
	}
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price - (int)(price * saleRatio); // 지불 가격 메서드 재정의
	}
	public String showCustomerInfo(){
    	return super.showCustomerInfo() + "담당 상담원 번호는 " + agentID + "입니다.";
    }	// 고객 정보 출력 메서드 재정의

	public int getAgentID() {
		return agentID;
	}
	}

VIP 고객 클래스에서 calcPrice() 메서드와 showCustomerInfo() 메서드를 재정의했다.

VIP 고객 클래스에서는 할인율을 반영한 지불 가격을 반환하고 담당 상담원 번호까지 출력한다. 

 

고객관리 프로그램을 완성해보자

public class CustomerTest1 {

	public static void main(String[] args) {

		Customer customerLee = new Customer();
		customerLee.setCustomerID(10100);
		customerLee.setCustomerName("이병헌");
		customerLee.bonusPoint = 1000;
		System.out.println(customerLee.showCustomerInfo());
		
        // VIPCustomer를 Customer형으로 선언
	Customer customerChoi = new VIPCustomer(10101, "최민식",100);
	customerChoi.bonusPoint = 1000;
        System.out.println(customerChoi.showCustomerInfo());
        System.out.println("======= 할인율과 보너스 포인트 계산=======");
        
        int price = 10000;
        int leePrice = customerLee.calcPrice(price);
        int choiPrice = customerChoi.calcPrice(price);
        
        System.out.println(customerLee.getCustomerName() + "님이"+
        leePrice + "원 지불하셨습니다.");
        System.out.println(customerLee.showCustomerInfo());
        System.out.println(customerChoi.getCustomerName() + "님이"+
        choiPrice + "원 지불하셨습니다.");
        System.out.println(customerChoi.showCustomerInfo());
        
	}
}

출력 결과

출력 결과를 보면 10000원짜리 상품을 구입했을 때 등급에 따라 다른 할인율과 포인트 적립이

이루어지는 것을 알 수 있다. 고객의 자료형은 Customer형으로 동일하지만 할인율과 보너스 포인트는

각 인스턴의 메서드에 맞게 계산됐다. 상위 클래스와 하위 클래스는 같은 상위 클래스 자료형으로 

선언되어 생성할 수 있지만 재정의된 메서드는 각각 호출되고 이름이 같은 메서드가 서로 다른 역할을

구현하고 있다는 것을 알 수 있다.

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

GOLD 고객 등급을 추가한다. 혜택은 아래와 같다.

-제품을 살 때는 항상 10% 할인

-보너스 포인트를 2% 적립

-담당 전문 상담원은 없다.

public class GoldCustomer extends Customer{

	double saleRatio;
	
	public GoldCustomer(int customerID, String customerName ) {
		
		super( customerID,  customerName);
		customerGrade = "GOLD"; // 골드등급
		bonusRatio = 0.02; // 포인트 적립율
		saleRatio = 0.1; // 할인율
	}
    
	@Override
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio // 재정의한 메서드
		return price - (int)(price * saleRatio);
	}
	
}

GoldCustomer 클래스는 calcPrice() 메서드만 재정의했다.

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

배열로 고객 5명 구현하기

여러 등급의 고객을 한 번에 관리할 수 있도록 구현해 보자.

5의 고객은 VIP 1명, GOLD 2명, SILVER 2명 

고객 인스턴스가 총 5개이므로 객체 배열 ArrayList에 자료형 Customer로 지정하고

VIP, GOLD 모두 Customer에서 상속받기 때문에 Customer형으로 선언한다.

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

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

 

Customer 클래스와 VIPCustomer 클래스는 이전 예제와 동일하므로 생략한다.

import java.util.ArrayList;

public class CustomerTest {

	public static void main(String[] args) {

		ArrayList<Customer> customerList = new ArrayList<Customer>();
		
		Customer customerLee = new Customer(10010, "이병헌");
		Customer customerChoi = new Customer(10011, "최민식");
		GoldCustomer customerHa = new GoldCustomer(10012, "하정우");
		GoldCustomer customerJo = new GoldCustomer(10012, "조진웅");
		VIPCustomer customerKim = new VIPCustomer(10014, "김성균", 10);
		
		customerList.add(customerLee);
		customerList.add(customerChoi);  // ArrayList의 add 속성을 사용해 
		customerList.add(customerHa);     // 객체 배열에 고객 추가
		customerList.add(customerJo);
		customerList.add(customerKim);
		
		System.out.println("======고객정보 출력========");
		for(Customer customer : customerList) { // 향상된 for문 사용
			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.showCustomerInfo());
		}
	}

}

결과 출력

 

	int price = 10000;
	for(Customer customer : customerList) { 
		int cost = customer.calcPrice(price); 
		System.out.println(customer.getCustomerName()+ "님이"+ cost + " 를 지불하셨습니다.");
		System.out.println(customer.showCustomerInfo());
}

for(Customer customer : customerList) 문장은 CustomerList 배열의 요소를 하나씩 가져와서

Customer형 변수가 넣는다. ArrayList 배열에 저장할 때 Customer형으로 형 변환해서 추가했기 때문에

배열의 요소를 가져올 때도 Customer형으로 가져오게 된다. 각 인스턴스가 calcPrice() 메서드를 호출하면

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

상속과 다형성을 잘 활용하면 복잡한 코드를 간결하게 줄일 수 있고 확장성 있는 프로그램을 구현할 수 있다.

상속은 언제 사용하면 좋은가?

Customer 클래스에 VIP 고객 내용을 함께 구현한다면?

추가 기능을 이렇게 구현한다면 코드가 굉장히 복잡해진다. 일반등급 고객이 사용하지 않는 속성이나

서비스 내용까지 추가해야 하기 때문이다. 

Customer 클래스에 모든 등급의 내용을 넣어 구현한 예를 보자

if(customerGrade == "VIP"){ // 할인가능 / 적립 많음
}
else if(customerGrade == "GOLD"){ // 할인가능 / 적립은 중간
}
else if(customerGrade == "SILVER"){ // 적립만가능
}

calcPrice() 메서드 등 다른 여러 메서드에서도 등급에 따라 구현이 필요하다면

if-else if- else문이 많이 사용될 것이다. 이러한 경우 고객등급이 하나라도 추가되거나 삭제될 시

유지보수가 매우 복잡해진다.

상속을 사용한다면 공통으로 사용하는 부분은 상위 클래스인 Cutomer 클래스에 구현하고,

각 등급별 고객의 내용은 각각 하위 클래스에 구현한다. 새로운 고객이 추가되어도 기존 코드를 거의

수정하지 않고 새로운 클래스를 추가 가능하다. (확장성 , 유지보수 유리) 

IS - A 관계  HAS - A 관계?

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

상속은 IS - A 관계에서 가장 효율적이다. 일반 클래스를 점차 구체화하는 상황에서 상속을 사용한다.

 

HAS - A 관계란 한 클래스가 다른 클래스를 소유한 관계입니다. 재사용할 수 있는 코드가 있다고 해서

무조건 상속을 받는 것은 아니다. 상속을 사용하면 클래스 간의 결합도가 높아져 상위 클래스의 변화가 

하위 클래스에 미치는 영향이 크다. 상속은 '일반적인 클래스'와 '구체적인 클래스'의 관계에서

구현하는 것 이 맞다.

 

 

728x90
Comments