728x90
배열과 다형성?
- 앞에서 배운 상속과 다형성을 활용하면 프로그램을 유지보수하는 데 매우 편리합니다.
- 이때 배열과 함께 사용하면 여러 하위 클래스 자료형을 상위 클래스 자료형으로 한꺼번에 관리할 수도 있습니다.

배열과 다형성 활용하기 (고객 분류에 GOLD 등급 추가 & 배열로 고객 구현)
- 고객 등급 분류를 더 세분화 하기 위해 GOLD 등급을 추가하고자 한다고 생각해봅시다.
- GOLD 의 혜택은 다음과 같습니다.
- 할인율 10%, 보너스 포인트 2% 적립, 담당 전문 상담원은 없다.
- 위 내용을 기반으로 Customer 클래스를 상속받아 GoldCustomer 클래스를 만들어보겠습니다.

- Customer 클래스를 상속받는 계층은 위와 같습니다.
- 우선 polymorphism 패키지에 있는 Customer 클래스의 변수를 모두 protected 형으로 선언하여 witharraylist 패키지 내부에 있는 GoldCustomer 클래스가 사용할 수 있도록 만들었습니다.
package witharraylist;
import polymorphism.*;
public class GoldCustomer extends Customer{
double saleRatio;
public GoldCustomer() {
super();
}
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명 구현하기
- 이제 여러 등급의 고객을 한 번에 관리할 수 있도록 구현해 보겠습니다.
- 예제 시나리오 : VIP 1명, GOLD 2명, SILVER 2명이 각각 10,000원짜리 상품을 구매했을 때 결과를 출력하세요.
- 고객 인스턴스가 총 5개이므로 배열에 넣어서 관리하면 편리할겁니다.
- 객체 배열 ArrayList 는 자료형을 지정하여 선언해야 합니다.
- 우리가 사용할 클래스는 Customer, GoldCustomer, VIPCustomer 세 종류입니다.
- 배열의 자료형을 Customer 로 지정하면, VIPCustomer 와 GoldCustomer 클래스 모두 Customer 에서 상속받은 클래스이므로 이 배열에서 Customer, GoldCustomer, VIPCustomer 를 모두 사용할 수 있습니다.
- 이 배열에 Customer 하위 클래스의 인스턴스가 추가될 때는 모두 Customer 형으로 묵시적 형 변환이 발생합니다.
package witharraylist;
import polymorphism.*;
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 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);
for (Customer customer : customerList) {
customer.showCustomerInfo();
}
int price=10000;
for (Customer customer : customerList) {
int cost = customer.calcPrice(price);
System.out.println(customer.getCustomerName() + " 님이" + cost + "원 지불하셨습니다.");
}
for (Customer customer : customerList) {
System.out.println(customer.showCustomerInfo());
}
}
}
- 우선 Customer 클래스에서 customerName 변수는 protected 로 선언했습니다.
- 재미있는 것은 현재 배열에 있는 객체들이 Customer 클래스이거나 Customer 클래스를 상속받은 하위 클래스이더라도 protected 로 선언된 customerName 변수에는 접근할 수 없다는 것입니다.
- 그 이유는 어찌되었건 studentTest 클래스는 Customer 를 상속받지 않은 클래스이기 때문에 결국에는 customerName 변수에 접근할 권한이 없습니다.
- 따라서 지금 클래스에서 customerName 변수에 접근하기 위해서는 Customer 클래스에서 getter 메서드를 생성해주어야 합니다.
- 위의 코드를 한번 자세히 살펴보겠습니다.
- Customer 형으로 객체 배열 ArrayList 를 선언하였습니다.
- 그런 다음 Customer 클래스로 형 변환된 Customer, VIPCustomer, GoldCustomer 클래스의 인스턴스들을 ArrayList 배열에 추가했습니다.
- 22행에서는 향상된 for 문을 사용하여 배열에 추가된 인스턴스들의 showCustomerInfo( ) 메서드를 실행시켰습니다.
- showCustomerInfo( ) 가 재정의된 클래스들은 인스턴스의 showCustomerInfor( ) 메서드가 실행됩니다.
- 27행에서 수행되는 calcPrice 도 마찬가지입니다.
- 중요한 것은 만약 재정의한 메서드가 가상 메서드 방식에 의해 자동으로 호출되지 않는다면 if-else if 문을 사용하여 각 자료형에 적합한 코드를 따로 구현해야 할 것입니다. 또한 새로운 등급의 고객이 추가로 필요한 경우에는 또 다른 조건을 구현해야 하므로 코드의 유지보수가 어려워집니다.
- 이런 경우에 상속과 다형성을 잘 활용하면 복잡한 코드를 간결하게 줄일 수 있고 확장성 있는 프로그램을 구현할 수 있습니다.
상속은 언제 사용할까? (상속을 사용 안 할 때의 문제점으로 살펴보는 상속의 장점, 상속은 언제나 사용하는 것이 맞을까?)
- 이전 예제에서 살펴보았던 VIP 고객 등급을 추가하는 문제를 생각해 보겠습니다.
- 이미 Customer 클래스가 구현되어 있는데 추가 요구 사항이 생긴 것입니다.
- 간단하게 해결하는 방법은, Customer 클래스에 VIP 고객의 내용도 함께 구현하는 것입니다.
- 그런데 추가 기능을 이렇게 구현하면 Customer 클래스의 코드가 굉장히 복잡해집니다.
- 그 이유는 일반 등급 고객이 사용하지 않는 속성(상담원 ID, 할인율 등) 뿐만 아니라 VIP 고객만을 위한 서비스(기능) 까지 추가해야 하기 때문입니다.
if (customerGrade == 'VIP') {
}
else if (customerGrade == "GOLD") {
}
else if (customerGrade == "SILVER") {
}
- 위의 코드는 모든 등급의 내용을 넣었을 때 Customer 클래스의 모습을 보여주는 코드입니다.
- 고객 등급에 따라 다르게 구현해야 하기 때문에 if-else if-else 문을 사용합니다.
- calcPrice( ) 메서드 뿐만 아니라 여러 다른 메서드에서도 등급에 따른 구현이 필요하다면 클래스 전체에서 이러한 if-else if-else 문이 많이 사용될 겁니다.
- 이렇게 된다면 고객의 등급이 하나라도 추가되거나 삭제되면 유지보수가 매우 복잡해집니다.
- 앞에서 학습했듯이 상속을 사용하면 모든 등급에서 공통으로 상요하는 코드 부분은 상위 클래스인 Customer 클래스에 구현하고, 각 등급별 고객의 내용은 각각의 하위 클래스에 구현합니다.
- 또한 새로운 등급의 고객이 추가되더라도 기존의 코드를 거의 수정하지 않고 새로운 클래스를 추가할 수 있습니다.
- 따라서 프로그램이 확장성 있고 유지보수하기 좋습니다.
상속을 항상 사용하는 것이 좋을까?
- 당연히 그렇지 않습니다.
- 상속은 흔히 IS-A 관계일 때 사용하는 것이 가장 효율적이다라고 말합니다.
- IS-A 관계 라는 용어가 있습니다. (is a relationship; inheritance)
- 'IS-A 관계' 란 일반적인 개념과 구체적인 개념의 관계입니다.
- 즉 '사람은 포유류이다'와 같은 관계입니다.
- 일반 클래스를 점차 구체화하는 상황에서 상속을 사용하는 것입니다.
- 상속을 사용하면 많은 장점이 있지만, 하위 클래스가 상위 클래스형에 종속되기 때문에 이질적인 클래스 간에는 상속을 사용하지 않는 것이 좋습니다.
- 단순히 코드를 재사용할 목적으로 서로 관련이 없는 개념의 클래스들을 상속 관계로 사용하는 것은 좋지 않은 코드 작성법입니다.
- 예시를 들어서 생각해보도록 하겠습니다.
package inheritance2;
public class Subject {
private int subjectID;
private int subjectName;
public int getSubjectID() {
return subjectID;
}
public void setSubjectID(int subjectID) {
this.subjectID = subjectID;
}
public int getSubjectName() {
return subjectName;
}
public void setSubjectName(int subjectName) {
this.subjectName = subjectName;
}
public void showSubjectInfo() {
System.out.println(subjectID + "," + subjectName);
}
}
- 과목을 나타내는 Subject 클래스가 있습니다.
- 모든 학생은 전공 과목을 가지고 있습니다. 그러므로 Subject 클래스에서 제공하는 여러 메서드를 활용하면 좋을 것 같습니다.
- 이런 경우 Student 클래스가 Subject 클래스를 상속받으면 되는 걸까요?
- 정답은 아닙니다.
- 왜냐하면 Subject 가 Student 를 포괄하는 개념의 클래스가 아니기 때문이고, 다른 말로 하면 IS-A 관계가 아닙니다.
- 게다가 Student 클래스를 상속받는 다른 클래스가 있을 수도 있습니다.
- Student 클래스는 Subject 클래스를 소유한 개념입니다.
- 이런 Student 와 Subject 의 관계를 'HAS-A 관계' 로 표현합니다. (has a relationship; association)
- HAS-A 관계란 한 클래스가 다른 클래스를 소유한 관계입니다.
- HAS-A 관계를 가지는 Subject 클래스는 Student 클래스에 포함되어 Student 의 멤버 변수로 사용하는 것이 적절합니다.
class Student {
Subject majorSubject;
}
- 상속을 코드 재사용 개념으로 이해하면 안 되는 이유가 여기에 있습니다.
- 재사용할 수 있는 코드가 있다고 해서 무조건 상속을 받는 것은 아닙니다.
- 상속을 하면 클래스 간의 결합도가 높아져서 상위 클래스의 변화가 하위 클래스에 미치는 영향이 크기 때문입니다.
- 따라서 상속은 '일반적인 클래스'와 '구체적인 클래스'의 관계(IS-A 관계)에서 구현하는 것이 맞습니다.
여러 클래스를 한 번에 상속 받을 수도 있을까?
- 한 클래스가 여러 클래스를 상속 받는 것을 다중 상속이라고 합니다.
- 자바 이전의 객체 지향 언어인 C++는 다중 상속을 지원했지만, 자바는 다중 상속을 지원하지 않습니다.
- 여러 클래스에서 상속을 받으면 그만큼 다양한 기능을 상속받을 수 있는 장점이 있는데, 받지 않는 이유는 무엇일까요?
- 그 이유는 다중 상속으로 인한 모호성 때문입니다.
- 예를 들어 두 개 이상의 상위 클래스에 같은 이름의 메서드가 정의 되어 있다면, 다중 상속을 받는 하위 클래스는 어떤 메서드를 상속받을지 모호해집니다.
- C++ 같은 언어에서는 문법적으로 이러한 문제를 해결하지만, 자바에서는 다중 상속의 장점보다는 모호함을 없애는 쪽을 선택해 다중 상속을 사용하지 않는 것입니다.
- 따라서 extends 예약어 뒤에 오는 클래스는 반드시 한 개여야 합니다.
'[그린컴퓨터] Server > JAVA(객체 지향 프로그래밍)' 카테고리의 다른 글
| 추상 클래스 { 추상 클래스란, 추상 클래스 구현하기, 추상 클래스를 만드는 이유 } (0) | 2023.05.31 |
|---|---|
| 다운 캐스팅과 instanceof { 다운 캐스팅이란, instanceof란 } (0) | 2023.05.31 |
| 다형성 { 다형성이란, 다형성의 장점 } (0) | 2023.05.31 |
| 메서드 오버라이딩 { 상위 클래스 메서드 재정의하기, VIP 고객 클래스의 제품 가격 계산 메서드 재정의하기, 묵시적 클래스 형 변환과 메서드 재정의, 가상 메서드 } (0) | 2023.05.30 |
| 상속에서 클래스 생성과 형 변환 { 하위 클래스가 생성되는 과정, 부모를 부르는 예약어 super, 상위 클래스로 묵시적 클래스 형 변환 } (0) | 2023.05.29 |