[그린컴퓨터] Server/JAVA(객체 지향 프로그래밍)

다형성 { 다형성이란, 다형성의 장점 }

Ben의 프로그램 2023. 5. 31. 11:08
728x90

다형성이란?

  • 지금까지 설명한 묵시적 클래스 형 변환과 가상 메서드를 바탕으로 객체 지향의 중요한 특성다형성(polymorphism) 을 학습해 봅시다. 
  • 다형성 polymorphism 이란 하나의 코드가 여러 자료형으로 구현되어 실행되는 것을 말합니다. 
  • 쉽게 말해 같은 코드에서 여러 실행 결과가 나오는 것을 의미합니다. 
  • 예제 코드를 통해서 알아보겠습니다. 
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 AnimalTest1 {

	public static void main(String[] args) {
		AnimalTest1 aTest = new AnimalTest1();
		aTest.moveAnimal(new Human());
		aTest.moveAnimal(new Tiger());
		aTest.moveAnimal(new Eagle());
	}
	
	public void moveAnimal(Animal animal) {
		animal.move();
	}
}

  • AnimalTest1 클래스에 moveAnimal( ) 메서드를 만들었습니다. 
  • moveAnimal( ) 메서드는 Animal 자료형을 매개변수로 받아서 move( ) 메서드를 실행합니다. 
  • 여기서 중요한 것은 Animal 형이 상위 클래스입니다. 
  • 따라서 매개변수가 전달되는 부분에 Human 인스턴스가 전달되었다면 우리가 이전에 배웠던 것처럼 묵시적 클래스 형 변환이 일어납니다. 
Animal ani = new Human( );
  • Animal 에서 상속받은 클래스가 매개변수로 넘어오면 모두 Animal 형으로 변환되므로 animal.move( ) 메서드를 호출할 수 있습니다. 
  • move( ) 라는 메서드는 하위 클래스에서 재정의된 함수이므로 가상 함수 원리에 따라서 Animal 의 move 가 아닌 실제 인스턴스의 메서드의 move( ) 메서드가 실행됩니다. 
  • animal.move( ) 라는 코드는 변함이 없지만 어떤 매개변수가 넘어왔느냐에 따라 출력문이 달라집니다. 이것이 바로 다형성 polymorphism 입니다. 

다형성의 장점

  • 위의 예시에서 다른 동물이 새로 추가되는 경우를 생각해 보겠습니다.
  • 새로운 동물도 Animal 클래스를 상속받아 구현하면 모든 클래스를 Animal 자료형 하나로 쉽게 관리할 수 있습니다.
  • 이것이 다형성을 활용한 프로그램의 확장성입니다. 
  • 만약 다형성이 없었다면 각 자료형에 따라 코드를 다르게 구현했을 것이고, 코드는 훨씬 복잡해지고 내용도 길어졌을 겁니다. 
  • 상위 클래스에서 공통 부분의 메서드를 제공하고, 하위 클래스에서는 그에 기반한 추가 요소를 덧붙여 구현하면 코드 양도 줄어들고 유지보수도 편리합니다. 
  • 또 필요에 따라 상속받은 모든 클래스를 하나의 상위 클래스로 처리할 수 있고 다형성에 의해 각 클래스의 여러 가지 구현을 실행할 수 있으므로 프로그램을 쉽게 확장할 수 있습니다. 
  • 이처럼 다형성을 잘 활용하면 유연하면서도 구조화된 코드를 구현하여 확장성 있고 유지보수 하기 좋은 프로그램을 개발할 수 있습니다. 

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

  • 앞선 예제에서 살펴보았던 VIP 고객의 혜택을 다형성으로 구현해 보겠습니다.
package polymorphism;

public class Customer {
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	int bonusPoint;
	double bonusRatio;
	
	public Customer() {
		initCustomer();
	}
	
	public Customer(int customerID, String customerName) {
		this.customerID = customerID;
		this.customerName = customerName;
		initCustomer();
	}
	
	private void initCustomer() {  // 멤버 변수의 초기화, 생성자에서만 호출하도록 private 접근자 설정
		customerGrade = "SILVER";
		bonusRatio = 0.01;
	}
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price;
	}
	
	public String showCustomerInfo() {
		return customerName + " 님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.";
	}
}
  • 위 코드에서 기존 코드와 달라진 점은 initCustomer( ) 메서드가 생성되었다는 점입니다. 
  • 이 메서드는 클래스의 멤버 변수를 초기화 하는데, Customer 클래스의 생성자 2개 모두에서 사용되는 부분이므로 메서드로 분리하여 호출하였습니다. 
  • 이번에는 VIP 고객 클래스를 수정하겠습니다. 
package polymorphism;

public class VIPCustomer extends Customer {
	private int agentID;
	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( ) 메서드를 재정의했습니다. 
  • 일반 고객 클래스에서 calcPrice( ) 메서드는 정가를 그대로 반환했지만, VIP 고객 클래스에서는 할인율을 반영한 지불 가격을 반환합니다. 
  • 또한 VIP 고객 클래스의 정보를 출력할 때는 Customer 클래스에서 출력한 정보에 더해서 담당 상담원 번호까지 출력합니다. 
public class CustomerTest {

	public static void main(String[] args) {
		Customer customerLee = new Customer(10010, "이순신");
		Customer customerKim = new VIPCustomer(10020, "김유신", 12345);
		customerLee.bonusPoint = 1000;
		customerKim.bonusPoint = 1000;
		
		System.out.println(showCustomerInfo(customerKim));
		System.out.println(customerKim.showCustomerInfo());
		System.out.println(showCustomerInfo(customerLee));
		System.out.println(customerLee.showCustomerInfo());
		System.out.println(customerKim.customerName + "님이 " + calcPrice(customerKim, 10000) + "원 지불하셨습니다.");
		System.out.println(customerKim.customerName + "님이 " + customerKim.calcPrice(10000) + "원 지불하셨습니다.");
		System.out.println(customerLee.customerName + "님이 " + calcPrice(customerLee, 10000) + "원 지불하셨습니다.");
		System.out.println(customerLee.customerName + "님이 " + customerLee.calcPrice(10000) + "원 지불하셨습니다.");
	}
	
	public static String showCustomerInfo(Customer customer) {
		return customer.showCustomerInfo();
	}
	
	public static int calcPrice(Customer customer, int price) {
		return customer.calcPrice(price);
	}
}
  • 위의 코드에서는 다형성을 2가지 방법으로 접근하여 사용했습니다. 
  • 여기서 중요한 것은 customerLee 와 customerKim 은 모두 Customer 형으로 선언되었지만, 할인율과 보너스 포인트는 각 인스턴스의 메서드에 맞게 가상 메서드가 작동하였다는 것입니다.
  • 즉 상속 관계에 있는 상위 클래스와 하위 클래스는 같은 사우이 클래스 자료형으로 선언되어 생성할 수 있지만 재정의된 메서드는 각가 호출될 뿐만 아니라 같은 이름의 메서드가 서로 다른 역할을 구현할 수 있습니다.