본문 바로가기
Java

refactor: 상속과 인터페이스 활용

by bkuk 2023. 4. 27.
해당 글은 NEXTSTEP 자바 플레이그라운드 with TDD, 클린 코드를 통해서 새롭게 배우고 내용을 기록한 글입니다.

 

1. 상황 가정


우리는 렌터카를 운영하고 있다.
현재 보유한 차량의 목록과 대수는 아래와 같다.

  • Sonata : 2대
  • Avante : 1대
  •  K5: 2대

차량을 예약할 때는 여행할 목적지의 이동거리를 입력받는다.
이때, 이동거리를 활용해 차량 별로 필요한 연료를 주입한다.

 

차량별 연비는 아래와 같다.

  • Sonata : 10km/리터
  • Avante : 15km/리터
  • K5 : 13km/리터

 

2. 공통 기능을 구현할 클래스의 메서드 목록을 나열해보자.

  • 리터당 이동 거리. 즉, 연비 ==> abstract double getDistancePerLiter();
  • 여행하려는 거리 ==> abstract double getTripDistance();
  •  차종의 이름 ==> abstract String getName();
  • 주입해야할 연료량
double getChargeQuantity() {
    return getTripDistance() / getDistancePerLiter();
}

 

최종적으로 주입해야 할 연료량을 확인할 수 있는 보고서를 작성해야한다.

 

3. 모든 Car 객체가 가져야 하는 공통 메서드를 선언하는 인터페이스를 정의하자.

모든 Car 객체가 가져야 하는 공통 메서드라...?

Report를 생성할때의 코드를 살펴보자.

public String generateReport() {
    StringBuilder report = new StringBuilder();

    for( AbstractCar car : touringCars ) {
        report.append(car.getName())
                .append(" : ")
                .append((int) car.getInjectFuelAmount()).append("리터")
                .append(System.lineSeparator());
    }
    return report.toString();
}

 

  • 'for-each' 루프를 이용해서 각 'car' 객체에 대해서 순회한다.
  • 순회하면서 'car' 객체의 'getName()' 메서드와 'getInjectFuelAmount()' 메서드를 호출하고 있다. 
  • 이를 공통 메서드로 분류하자.

 

그렇다면 Car 인터페이스를 아래와 같이 정의해보자.

public interface Car {
    String getName();

    double getChargeQuantity();
}

 

이를 구현하는 추상 클래스를 아래와 같이 구현해보자.

public abstract class AbstractCar implements Car {
    private double distancePerLiter;
    private double distance;
    
    public AbstractCar(int distance, int distancePerLiter) {
        this.distance = distance;
        this.distancePerLiter = distancePerLiter;
    }

    @Override
    public double getInjectFuelAmount() {
        return distance / distancePerLiter;
    }
}

 

특이한 점이 존재한다.


인터페이스 Car에서는 총 두개의 공통 메서드를 선언했다.
그 중 getChargeQuantity()만 오버라이드 했다.
이는 하위(자식)클래스에서 구현되는 추상 메서드로 남겨두는 것이다.

따라서 AbstractCar 클래스는 인터페이스 Car를 구현하지만, 추상메서드인 getName()을 오버라이드 하지 않았기 때문에 추상 클래스가 된다.

 

4. 하위(자식) 클래스에서 구현해보자.

 

상위(부모) 클래스에서 데이터 타입이 int형인 매개변수를 가지는 생성자를 정의했기 때문에 super() 예약어를 통해서 상위 클래스의 생성자를 호출해야 한다.


따라서, 아래와 같이 구현이 가능하다.

public class K5 extends AbstractCar {
    private static final int DISTANCE_PER_LITER = 13;
    private String name;

    public K5(int distanceToTravel) {
        super( distanceToTravel, DISTANCE_PER_LITER);
        this.name = "K5";
    }

    @Override
    public String getName() {
        return name;
    }
}

 

댓글