자바

[OOP 트레이닝] 주차장 시스템 - (3)

menuhwang 2023. 6. 30. 11:19

시리즈

요금 정산 시스템

무료 주차장에서 유료화하기로 결정했다.

 

요금 정산 시스템을 구현해 보자.

 

기존에는 주차장에 주차된 차량 목록만 저장했지만, 요금 계산을 위해 차량 정보와 입차 시간을 알아야 한다.

 

그리고 주차장의 입차, 출차를 관리하는 캐셔를 고용하기로 했다.

 

이 캐셔가 요금 계산까지 맡아주실 예정이다.

 

Time

시, 분 정보를 담고 분 단위로 계산해 줄 객체이다.

 

public class Time {
    private final int hour;
    private final int minute;

    private Time(final int hour, final int minute) {
        this.hour = hour;
        this.minute = minute;
    }

    public static Time of(final int hour, final int minute) {
        return new Time(hour, minute);
    }

    public long getMinutes() {
        return (long) hour * 60 + minute;
    }
}

 

Parking

차량 목록을 저장하던 Set<Car> cars를 지우고 입차 시간을 담는 history 필드를 생성했다.

 

입차 시간은 분 단위로 계산해 저장해 줄 계획이다.

 

public class Parking {
    ...
    private final HashMap<Car, Long> history = new HashMap<>();
    ...
}

 

ChargePolicy

요금 정책을 담고 요금을 계산해 주는 객체이다.

 

기본 이용 시간, 기본 이용 요금, 추가 이용 단위 시간, 단위 시간당 요금 필드를 가지고 있다.

 

이용 시간이 기본 이용 시간 이하라면 기본요금만 발생하고, 초과하면 초과 시간에 따라 단위 시간당 요금이 추가 발생한다.

 

public class ChargePolicy {
    private final int basicTime;
    private final int basicCharge;
    private final int unitTime;
    private final int unitCharge;

    public ChargePolicy(final int basicTime, final int basicCharge, final int unitTime, final int unitCharge) {
        this.basicTime = basicTime;
        this.basicCharge = basicCharge;
        this.unitTime = unitTime;
        this.unitCharge = unitCharge;
    }

    public long calculate(final long minutes) {
        if (minutes <= basicTime) return basicCharge;
        long overMinutes = minutes - basicTime;
        long unit = overMinutes % unitTime == 0 ? overMinutes / unitTime : (overMinutes / unitTime) + 1;
        return basicCharge + unit * unitCharge;
    }
}

 

Cashier

입출차를 관리하고 요금 계산을 위해 캐셔를 고용하기로 했다.

 

캐셔 객체는 요금 정책이 무엇인지 알아야 하고, 입출차 관리를 위해 주차장의 입출차 장부를 가지고 있어야 한다.

 

public class Cashier {
    private ChargePolicy chargePolicy;
    private HashMap<Car, Long> history;

    public Cashier() {
    }

    public void hired(ChargePolicy chargePolicy, HashMap<Car, Long> history) {
        this.chargePolicy = chargePolicy;
        this.history = history;
    }
}

 

입출차 관리도 캐셔가 맡는 것으로 한다.

 

출차할 때 이용 시간과 요금을 안내해 주도록 했다.

public class Cashier {
    ...
    public void income(final Car car, final Time time) {
        // 차량 번호가 같은 경우 무반응
        if (history.containsKey(car)) return;
        writeHistory(car, time.getMinutes());
    }

    private void writeHistory(final Car car, final long minutes) {
        history.put(car, minutes);
    }

    public void outcome(final Car car, final Time time) {
        long minutes = time.getMinutes() - history.get(car);
        long charge = chargePolicy.calculate(minutes);
        notifyCharge(minutes, charge);
        removeHistory(car);
    }

    private void removeHistory(final Car car) {
        history.remove(car);
    }
    
    private void notifyCharge(final long minutes, final long charge) {
        System.out.println("[Cashier]");
        System.out.printf("이용 시간: %d[min]\n요금: %d[원]\n", minutes, charge);
    }
}

 

Parking

주차장 개업 시 요금 정책을 설정하고, 캐셔를 고용하도록 수정한다.

 

또한, 이제부터 입출차 관리는 캐셔가 할 예정이므로 입출자 로직 수정이 필요하다.

 

그리고 만약 캐셔가 고용되지 않은 상태에서는 입출차를 못하도록 했다.

 

public class Parking {
    private final ChargePolicy chargePolicy;
    private Cashier cashier;
    private final HashMap<Car, Long> history = new HashMap<>();

    private Parking(final ChargePolicy chargePolicy) {
        this.chargePolicy = chargePolicy;
    }

    public static Parking init(final ChargePolicy chargePolicy) {
        return new Parking(chargePolicy);
    }

    public void hire(final Cashier cashier) {
        this.cashier = cashier;
        cashier.hired(chargePolicy, history);
    }

    public void in(final Car car, final Time time) {
        if (!verifyStatus()) return;

        System.out.println("[Parking]");
        System.out.printf("입차 <-- %s\n", car);

        cashier.income(car, time);

        printInfo();
    }

    public void out(final Car car, final Time time) {
        if (!verifyStatus()) return;

        System.out.println("[Parking]");
        System.out.printf("출차 --> %s\n", car);

        cashier.outcome(car, time);

        printInfo();
    }

    public void printInfo() {
        ...
    }

    private boolean verifyStatus() {
        if (cashier == null) {
            System.out.println("[Parking]\n캐셔 고용이 필요합니다.\n");
            return false;
        }
        return true;
    }
}

 

! 경차, 전기차 할인 제도 추가

다음에는 경차, 전기차 할인 제도를 추가해 보도록 하겠다.

 

Car를 상속받아 경차, 전기차 객체를 만들지, 아니면 객체 안에 필드를 추가할지 고민해 봐야겠다.

 

ChargePolicy는 요금할인이 없는 주차장을 위해 요금할인 정책 객체를 새로 만들어봐야겠다.