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

상속이란 { 상속이란, 클래스의 상속, 상속을 사용하여 고객 관리 프로그램 구현하기 }

Ben의 프로그램 2023. 5. 29. 10:43
728x90

상속과 다형성을 배워야 하는 이유

  • 객체 지향 프로그램에서 지원하는 여러 기술을 활용하면 재사용성, 확장성이 좋고 유지보수가 수월한 프로그램을 구현할 수 있습니다. 
  • 상속과 다형성이 이에 해당합니다.
  • 또한 객체 지향 프로그램의 다형성을 활용하면 유연한 구조이 프로그램을 구현할 수 있습니다. 
  • 매우 중요한 내용이므로 필수적으로 학습하시길 바랍니다. 

상속이란? 

  • 객체 지향 프로그래밍의 중요한 특징 중 하나가 상속(inheritance)입니다.
  • 상속은 우리가 일반적으로 알 듯 무엇인가를 물려받는다는 의미입니다.
  • 자식이 부모로부터 무엇인가를 물려받으면 자식은 그것을 사용할 수 있습니다.
  • 객체 지향 프로그램에서도 마찬가지입니다. B 클래스가 A 클래스를 상속받으면 B 클래스는 A 클래스의 멤버 변수와 메서드를 사용할 수 있습니다. 
  • 객체 지향 프로그램은 유지 보수하기 편하고 프로그램을 수정하거나 새로운 내용을 추가하는 것이 유연한데, 그 기반이 되는 기술이 바로 상속입니다.

클래스의 상속

  • 상속과 관련하여 사용하는 용어와 문법에 대해 먼저 알아보겠습니다.

  • 우리가 생각하기에 상속을 하는 클래스에서 상속을 받는 클래스로 화살표가 갈 것 같지만, 클래스 간 상속을 표현할 때는 위 그림에 표현한 것처럼 상속받는 클래스에서 상속하는 클래스로 화살표가 갑니다.
  • 부모 클래스 parent class 는 '상위 클래스', 자식 클래스 child class 는 '하위 클래스' 라고 부릅니다. 
  • 예를 들어볼까요. 포유류는 사람, 개 보다 상위의 일반적인 개념입니다. 
  • 즉 사람은 포유류의 특징과 기능을 기본으로 더 많거나 다른 특징과 기능을 가지고 있습니다. 
  • 이렇게 상속 관계에서는 상위 클래스가 하위 클래스보다 일반적인 개념이고, 하위 클래스는 상위 클래스보다 구체적인 클래스가 됩니다. 

클래스 상속 문법

  • 자바 문법으로 상속을 구현할 때는 extends 예약어를 사용합니다. 
  • B extends A 라고 한다면 A 가 가지고 있는 속성이나 기능을 추가로 확장하여 B 클래스를 구현한다는 의미입니다.
  • 다른 말로 B 가 A 를 상속받는다고 얘기할 수 있습니다.

  • 위 그림 예시를 보면 이런 관계를 확인해볼 수 있습니다. 
  • 부모 클래스는 상위 클래스로 하위 클래스보다 일반적인 개념이고, 하위 클래스는 상위 클래스보다 구체적인 클래스인 것을 확인할 수 있습니다. 

상속을 사용하여 고객 관리 프로그램 구현하기

  • 간단한 프로그램을 만들면서 상속을 학습해 보겠습니다. 
  • 회사에서 고객 맞춤 서비스를 제공하기 위해 고객 관리 프로그램을 구현하려고 합니다. 
  • 우선 고객 클래스가 있어야 합니다. 
  • 이 예제에서는 고객 아이디, 이름, 고객 등급, 보너스 포인트, 보너스 포인트 적립 비율 속성으로 선언하겠습니다. 
package inheritance;

public class Customer {
	private int customerID;
	private String customerName;
	private String customerGrade;
	int bonusPoint;
	double bonusRatio;
	
	public Customer() {
		customerGrade = "SILVER";
		bonusRatio = 0.01;
	}
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price;
	}
	
	public String showCustomerInfo() {
		return (customerName + " 님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.");
	}
}
  • 우선 Customer 클래스의 멤버 변수와 멤버 메서드를 구현했습니다. 
  • 여기까지는 기존의 객체 지향 프로그램과 별반 차이가 없습니다. 
  • 이제 다음과 같은 상황을 생각해 보겠습니다. 
  • 새로운 고객 등급이 필요한 경우 (예제 시나리오)

    많은 구매를 지속적으로 해주는 단골 고객이 생김에 따라 우수 고객으로 선정하여 VIP 등급을 부여하고 다음과 같은 혜택을 제공할 예정입니다. 
    - 제품을 살 때는 항상 10% 할인해 줍니다.
    - 보너스 포인트를 5% 적립해 줍니다.
    - 담당 전문 상담원을 배정해 줍니다. 
  • 이 요구 사항을 어떻게 구현하면 좋을까요? 

    - 가장 간단한 방법은 이미 구현한 Customer 클래스에 VIP 고객을 구현하는데 필요한 변수와 메서드를 함께 구현하는 겁니다. 그런데, 이렇게 구현하기 시작하면 Customer 클래스의 코드가 복잡해집니다. 게다가 VIP 가 아닌 고객에게는 해당 코드들이 필요가 없기 때문에 메모리 낭비도 발생합니다. 결국 이런 경우에는 VIPCustomer 클래스를 따로 만드는 것이 좋습니다. 
package inheritance;

public class VIPCustomer {
	private int customerID;
	private String customerName;
	private String customerGrade;
	int bonusPoint;
	double bonusRatio;
	
	private int agentID;   // VIP 고객 관련 기능을 구현할 때만 필요한 멤버 변수
	double saleRatio;
	
	public VIPCustomer() {
		customerGrade = "VIP";
		bonusRatio = 0.05;
		saleRatio = 0.1;
	}
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price - (int)(price * saleRatio);   // 할인율 적용
	}
	
	public int getAgentID() {
		return agentID;
	}
	
	public String showCustomerInfo() {
		return customerName + " 님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.";
	}
}
  • 위와 같이 클래스를 구현했습니다.
  • 그런데, 클래스를 만들고 보니 앞에서 만든 Customer 클래스와 겹치는 멤버 변수와 메서드가 보입니다. 
  • 게다가 calcPrice( ) 메서드는 이름은 같은데 구현 내용은 다릅니다. 
  • 또한 VIP 고객도 큰 범주에서는 고객에 속합니다. 다만 일반 고객에 없는 추가 속성과 메서드가 있는 것입니다.
  • 바로 이런 경우에 상속을 사용합니다.  
  • 즉 Customer 클래스에 일반 고객의 속성과 기능이 이미 구현되어 있기 때문에, VIPCustomer 클래스는 Customer 클래스를 상속받고 VIP 고객에게 필요한 추가 속성과 기능을 구현하는 것입니다.
  • Customer 클래스를 상속한 VIPCustomer 클래스의 코드는 다음과 같습니다.
package inheritance;

public class VIPCustomerExtends extends Customer{
	private int agentID;
	double saleRatio;
	
	public VIPCustomerExtends() {
		customerGrade = "VIP";   // 상위 클래스에서 private 변수이므로 오류 발생
		bonusRatio = 0.05;
		saleRatio = 0.1;
	}
	
	public int getAgentID() {
		return agentID;
	}
}
  •  VIPCustomer 클래스의 코드가 간단해졌습니다
  • Customer 클래스에 이미 선언되어 있는 customerID, customerName, customerGrade, bonusPoint, bonusRatio 멤버 변수와 calcPrice( ), showCustomerInfo( ) 메서드는 상속을 받아서 사용할 것이기 때문에 구현하지 않아도 됩니다. 
  • 그런데, 위의 코드에는 2가지 문제가 있습니다. 

    - 첫 번째, customerGrade 변수는 상위 클래스에서 private 으로 접근 제어자를 설정한 변수입니다. 따라서 외부 클래스에서는 이 변수를 사용할 수 없습니다.
    - 두 번째, VIP 고객에게 제공하는 혜택인 할인율과 세일 가격을 어떻게 적용할지 구현하지 않았습니다. (오버라이딩 파트에서 보완할 예정)

상위 클래스 변수를 사용하기 위한 protected 예약어

  • 우선 customerGrade 변수에서 발생하는 오류부터 수정해 보겠습니다. 
  • 이 오류는 상위 클래스에 선언한 customerGrade 가 private 변수이기 때문에 발생합니다. 
  • 상위 클래스에서 작성한 변수나 메서드 중 외부 클래스에서 사용할 수 없지만 하위 클래스에서는 사용할 수 있도록 지정하는 예약어가 바로 protected 입니다. 
  • 상속받은 하위 클래스에서는 public 처럼 사용할 수 있습니다. 즉 상속된 하위 클래스를 제외한 나머지 욉 ㅜ클래스에서는 private 과 동일한 역할을 합니다. 
package inheritance;

public class Customer {
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	int bonusPoint;
	double bonusRatio;
	
	public int getCustomerID() {
		return customerID;
	}

	public void setCustomerID(int customerID) {
		this.customerID = customerID;
	}

	public String getCustomerName() {
		return customerName;
	}

	public void setCustomerName(String customerName) {
		this.customerName = customerName;
	}

	public String getCustomerGrade() {
		return customerGrade;
	}

	public void setCustomerGrade(String customerGrade) {
		this.customerGrade = customerGrade;
	}

	public Customer() {
		customerGrade = "SILVER";
		bonusRatio = 0.01;
	}
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price;
	}
	
	public String showCustomerInfo() {
		return (customerName + " 님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.");
	}
}
  • 위와 같이 Customer 클래스에 있는 private 변수를 다른 하위 클래스에서도 사용할 수 있도록 모두 protected 로 바꾸었습니다. 
  • 또한 해당 변수를 외부 클래스에서도 사용할 수 있도록 Getter 와 Setter 를 마련해 주었습니다. 
  • 위와 같이 protected 로 선언하면 VIPCustomer 에 있던 오류는 사라지게 됩니다.