Java 다형성, 역할과 구현하기 예제 - 자동차
코드 예제로 운전자와 자동차의 관계를 통해 객체 지향적으로 개선하면서 작성해보도록 한다.

Driver가 K5Car 운전하는 프로그램이다.
코드는 다형성을 사용하지 않도록 한다.
K5Car.java
package k5;
public class K5Car {
public void startEngine() {
System.out.println("K5 엔진 켜기");
}
public void offEngine() {
System.out.println("K5 엔진 끄기");
}
public void pressAccelerator() {
System.out.println("K5 엑셀 가속하기");
}
}
Driver.java
package k5;
public class Driver {
// 초기 값 null, 밖에서 new 키워드로 객체를 생성 필요
private K5Car k5Car;
// 밖에서 호출되어야 생성함, new 연산자 생성한 객체 필요.
public void setK5Car(K5Car k5Car) {
this.k5Car = k5Car;
}
public void drive() {
System.out.println("자동차 운전하기");
//엔진 켜기
k5Car.startEngine();
k5Car.pressAccelerator();
k5Car.offEngine();
}
}
CarMain.java
package k5;
public class CarMain {
public static void main(String[] args) {
Driver driver = new Driver();
K5Car k5car = new K5Car();
driver.setK5Car(k5car);
driver.drive();
}
}
실행 결과
자동차 운전하기
K5 엔진 켜기
K5 엑셀 가속하기
K5 엔진 끄기
바깥 main 함수에서 new 연산자로 객체를 생성하고 setK5Car 메소드로 객체 참조 값을 넘겨서 실행하는 코드이다. 이어서 인스턴스 기능 driver() 메소드를 실행한다.
메모리 상황

새로운 ElectricCar 생성
새로운 model Y 전기차를 추가하는 요구사항이 온 경우 Driver 코드를 변경해주어야 한다.
Driver 는 K5Car ModelYCar 운전할 수 있지만 동시에 하지 않는다.

신규코드 ModelYCar.java
package k5;
public class ModelYCar {
public void startEngine() {
System.out.println("Model Y 엔진 켜기");
}
public void offEngine() {
System.out.println("Model Y 엔진 끄기");
}
public void pressAccelerator() {
System.out.println("Model Y 엑셀 가속하기");
}
}
변경코드 Driver.java
package k5;
public class Driver {
private K5Car k5Car;
private ModelYCar modelYCar; // 코드 추가
public void setK5Car(K5Car k5Car) {
this.k5Car = k5Car;
}
// 코드 추가
public void setModelYCar(ModelYCar modelYCar) {
this.modelYCar = modelYCar;
}
public void drive() {
System.out.println("자동차 운전하기");
if (k5Car != null) {
//엔진 켜기
k5Car.startEngine();
//엑셀 가속하기
k5Car.pressAccelerator();
//엔진 종료
k5Car.offEngine();
}
if (modelYCar != null) {
//엔진 켜기
modelYCar.startEngine();
// 엑셀 가속하기
modelYCar.pressAccelerator();
//엔진 종료
modelYCar.offEngine();
}
}
}
CarMain.java
package k5;
public class CarMain {
public static void main(String[] args) {
Driver driver = new Driver();
K5Car k5car = new K5Car();
driver.setK5Car(k5car);
driver.drive();
ModelYCar modelYCar = new ModelYCar();
driver.setK5Car(null);
driver.setModelYCar(modelYCar);
driver.drive();
}
}
- 신규 ModelYCar 차량을 추가하고 운전하는 코드이다.
driver.setK5Car(null)
기존 참조 값 제거driver.setModelYCar(modelYCar)
새로운 참조 추가driver.drive()
호출하여 운전하기
실행 결과
자동차 운전하기
K5 엔진 켜기
K5 엑셀 가속하기
K5 엔진 끄기
자동차 운전하기
Model Y 엔진 켜기
Model Y 엑셀 가속하기
Model Y 엔진 끄기
메모리 상황

새로운 차량이 추가되어 Driver 코드가 많이 변경 되었다. 운전하는 차량이 계속 늘어나면 코드는 점점 늘어나고 복잡해질 것이다.
이러한 문제가 계속 발생되는 원인은 역할과 구현이 분리되어 있지 않고 Driver 클래스가 모든 것을 담당하고 있다.
추상 클래스 추가하기
운전자이자 클라이언트 코드인 Driver 클래스가 코드 변경이 있었다.
다형성을 추가하여 클라이언트 코드 변경이 없어도 새로운 차량을 추가할 수 있는 코드로 개선한다.
Car 추상화 추가하기

- Driver 운전자는 추상화된 자동차 Car 역할과 의존하는 관계이다.
- Driver 클래스는 Car 인터페이스를 참조
- 클래스 의존 관계는 클래스가 어떤 클래스를 알고 있는가? 관계이다.
- 예를 들어, Driver 클래스는 Car 클래스만 알고 K5Car, MedelYCar 클래스는 모른다.
- Car 역할은 인터페이스이다. K5Car, ModelYCar 클래스가 인터페이스를 구현한다.
Car.java
package k5;
public interface Car {
void startEngine();
void pressAccelerator();
void offEngine();
}
K5Car.java
package k5;
public class K5Car implements Car {
@Override
public void startEngine() {
System.out.println("K5 엔진 켜기");
}
@Override
public void pressAccelerator() {
System.out.println("K5 엑셀 가속하기");
}
@Override
public void offEngine() {
System.out.println("K5 엔진 끄기");
}
}
ModelYCar
package k5;
public class ModelYCar implements Car {
@Override
public void startEngine() {
System.out.println("Model Y 엔진 켜기");
}
@Override
public void offEngine() {
System.out.println("Model Y 엔진 끄기");
}
@Override
public void pressAccelerator() {
System.out.println("Model Y 엑셀 가속하기");
}
}
Driver.java
package k5;
public class Driver {
private Car car;
public void setCar(Car car) {
System.out.println("선택한 자동차 불려옵니다. " + car);
this.car = car;
}
public void drive() {
System.out.println("선택한 자동차 운전하기");
car.startEngine();
car.pressAccelerator();
car.offEngine();
}
}
- Driver 멤버 변수는
Car car
변수만 갖고 있다. setCar(Car car)
멤버 변수로 자동차를 가져온다.
외부에서 이 메소드를 호출해야만 자동차를 운전하거나 할 수 있다.driver()
Car 인터페이스 구현된 기능들을 불려와 운전한다.
CarMain.java
package k5;
public class CarMain {
public static void main(String[] args) {
Driver driver = new Driver();
Car k5car = new K5Car();
driver.setCar(k5car);
driver.drive();
Car modelYCar = new ModelYCar();
driver.setCar(modelYCar);
driver.drive();
}
}
출력 결과
선택한 자동차 불려옵니다. k5.K5Car@4e50df2e
선택한 자동차 운전하기
K5 엔진 켜기
K5 엑셀 가속하기
K5 엔진 끄기
선택한 자동차 불려옵니다. k5.ModelYCar@7cc355be
선택한 자동차 운전하기
Model Y 엔진 켜기
Model Y 엑셀 가속하기
Model Y 엔진 끄기
코드 K5Car 불려오기
- Driver 와 함께 K5Car 객체를 생성한다.
- driver.setCar(...) 호출하여 Car car 변수를 K5Car 인스턴스로 참조하였다.
- drive() 호출하여 참조된 car 변수 Car타입에서 메소드 오버라이딩에 의해 K5Car 클래스의 startEngine(), pressAccelerator(), offEngine() 메소드를 호출한다.
코드 ModelYCar 불려오기
- Driver 와 함께 ModelYCar 객체를 생성한다.
- driver.setCar(...) 호출하여 Car car 변수를 ModelYCar 인스턴스로 참조하였다.
- drive() 호출하여 참조된 car 변수 Car타입에서 메소드 오버라이딩에 의해 ModelYCar 클래스의 startEngine(), pressAccelerator(), offEngine() 메소드를 호출한다.