Java 객체지향 클래스를 설계해보자

Doyeon·2023년 2월 3일
0
post-thumbnail

객체지향 클래스를 설계해보자

자바는 객체지향의 대표적인 언어로, 데이터와 기능을 묶어 하나의 객체를 만들어 프로그램을 설계할 수 있다. 속성과 기능을 가진 객체는 다른 객체의 메서드나 필드를 호출하며 상호작용을 할 수 있다.

오늘은 버스 클래스와 택시 클래스를 만들고 공통 기능을 추상 클래스와 인터페이스로 만들어 구현해보려고 한다.


step0. 클래스 속성과 기능 정의하기

클래스는 속성과 기능의 집합이다. 어떤 속성과 기능이 필요한지 먼저 정리해보자.

1. Bus

  • 속성
    • 버스 번호, 최대 승객 수, 현재 승객 수, 요금, 주유량, 현재 속도, 상태(운행, 차고지행)
  • 기능
    • 운행
    • 버스 상태 변경
      • 객체 최초 생성 시 상태 → “운행”
      • 주유량 떨어지거나, 운행 종료 → “차고지행”
      • 주유량 10 미만 → “주유가 필요하다” 메시지 출력
    • 승객 탑승
      • 탑승 시 현재 승객 수 증가
      • ‘최대 승객 수’ 이하까지 + ‘운행’중인 경우만 탑승 가능
    • 속도 변경
      • 주유 상태 체크 → 10 이상이어야 운행 가능
      • 주유량 10 미만 → “주유량을 확인해주세요” 메시지 출력
      • 속도 추가, 감소 기능

2. Taxi

  • 속성
    • 택시 번호, 최대 승객 수, 현재 승객 수, 목적지, 기본거리, 목적지까지 거리, 기본 요금, 거리당 요금, 주유량, 현재 속도, 상태(운행중, 일반, 운행불가)
  • 기능
    • 운행
      • 시작 전 주유상태 체크 → 주유량 10 이상이어야 운행 가능
    • 승객 탑승
      • 택시 상태 ‘일반’일때만 가능 → 그 외 ‘탑승 불가’
      • 승객 태우면 택시 상태는 ‘운행중’
    • 속도 변경
      • 속도 추가, 감소 기능
    • 거리당 요금 추가
      • 기본거리 넘을 경우 거리당 요금 추가하여 계산
    • 요금 결제
      • 최종 요금 출력

step1. Bus class 만들기

클래스의 속성은 변수로, 기능은 메서드로 만들어주면 된다.

1. 변수설정

private final int bus_number;     // 버스 번호
private int max_passenger;  // 최대 승객 수
private int cur_passenger;  // 현재 승객 수
private int fee;            // 요금(승객 수 * 기본요금)
private int fueling;        // 주유량
private int speed;          // 현재 속도
private String status;      // 상태

static int bus_idx = 0;     // 버스 객체 생성 시, 버스 번호를 고유번호로 생성하기 위해 필요한 클래스 변수
static final int BASIC_FEE = 1000; // 1인당 기본요금 설정

2. 생성자

public Bus() {
    bus_number = ++bus_idx;
    max_passenger = 30;
    cur_passenger = 0;
    fee = 0;
    fueling = 100;
    speed = 50;
    status = "운행";

    System.out.println(this.bus_number + "번 버스가 생성되었습니다.");
    }
}
  • 버스 번호는 고유값으로 생성
  • 초기값 - 최대 승객 수 30, 현재 승객 수 0, 현재 요금 0, 주유량 100, 속도 50, 상태 "운행"

3. 메서드

  • String toString() : 버스 객체 상태를 쉽게 보기 위해 toString() 메서드를 오버라이딩했다.
  • int getFueling() : 현재 주유량을 return
  • void chgFueling(int fueling) : 주유량을 입력받은만큼 넣어주고 10 미만인지 체크한다.
  • void checkFueling() : 현재 주유량이 10 미만이라면 메시지를 출력, 0이라면 상태를 "차고지행"으로 바꾼다.
  • void setStatus(String status) : "종료" 또는 "운행"을 입력받아 현재 상태를 바꾼다. 상태를 바꿀 때 주유량을 체크한다.
  • void addCur_passenger(int passenger) : 입력받은 passenger 수만큼 현재 승객 수를 업데이트한다. 단, 최대 승객 수를 넘지 않아야 하고 운행중인 버스여야 한다.
  • void chgSpeed(int speed) : 속도를 입력받아 현재 속도를 업데이트한다. 단, 주유량이 10 이상인 경우 속도 업데이트가 가능하며, 미만인 경우 경고 메시지를 출력한다.

4. 코드

  • class Bus

    public class Bus {
        private final int bus_number;     // 버스 번호
        private int max_passenger;  // 최대 승객 수
        private int cur_passenger;  // 현재 승객 수
        private int fee;            // 요금
        private int fueling;        // 주유량
        private int speed;          // 현재 속도
        private String status;      // 상태
    
        static int bus_idx = 0;
        static final int BASIC_FEE = 1000;
    
        public Bus() {
            bus_number = ++bus_idx;
            max_passenger = 30;
            cur_passenger = 0;
            fee = 0;
            fueling = 100;
            speed = 50;
            status = "운행";
    
            System.out.println(this.bus_number + "번 버스가 생성되었습니다.");
        }
    
        public String toString() {
            return "(버스 번호 : " + bus_number +
                    ", 최대 승객 수 : " + max_passenger + ", 현재 승객 수 : " + cur_passenger +
                    ", 요금 : " + fee + ", 주유량 : " + fueling +
                    ", 현재 속도 : " + speed + ", 상태 : " + status + ")";
        }
    
        public int getFueling() {
            return this.fueling;
        }
    
        public void chgFueling(int fueling) {
            this.fueling += fueling;
            checkFueling();
        }
    
        public void checkFueling() {
            if (this.fueling < 10) {
                System.out.println("주유가 필요합니다.");
                this.status = "차고지행";
            }
        }
    
        public void setStatus(String status) {
            if (status.equals("종료")) {
                this.status = "차고지행";
            } else if (status.equals("운행")) {
                this.status = "운행";
            } else {
                System.out.println("운행 / 종료 두 상태로 변경할 수 있습니다.");
            }
            checkFueling();
        }
    
        public void addCur_passenger(int passenger) {
            if (this.cur_passenger + passenger > this.max_passenger)
                System.out.println("최대 승객 수는 " + this.max_passenger + "명 입니다. 현재 승객 수는 최대 승객 수를 초과할 수 없습니다.");
    
            if (this.status.equals("차고지행"))
                System.out.println("현재 버스 상태는 " + this.status + " 입니다. 버스가 운행중일 때만 승객이 탑승할 수 있습니다.");
    
            if (this.cur_passenger + passenger <= this.max_passenger && this.status.equals("운행")) {
                this.cur_passenger += passenger;
            }
    
            this.fee = BASIC_FEE * this.cur_passenger;
        }
    
        public void chgSpeed(int speed) {
            if (getFueling() < 10) {
                System.out.println("주유량을 확인해주세요.");
                return;
            }
    
            this.speed += speed;
        }
    
    }
  • Main

    public class Main {
        public static void main(String[] args) {
            // 버스 번호 자동 생성
            System.out.println("<버스 생성>");
            Bus bus1 = new Bus();
            Bus bus2 = new Bus();
    
            System.out.println(bus1);
            System.out.println(bus2);
    
            // 승객 탑승
            System.out.println("\n" + "승객 2명 추가");
            System.out.println("변경전 -> " + bus1);
            bus1.addCur_passenger(2);
            System.out.println("변경후 -> " + bus1);
    
            // 주유량 변경
            System.out.println("\n" + "주유량 변경 (-50)");
            System.out.println("변경전 -> " + bus1);
            bus1.chgFueling(-50);
            System.out.println("변경후 -> " + bus1);
    
            // 상태 변경
            System.out.println("\n" + "차고지행으로 변경");
            System.out.println("변경전 -> " + bus1);
            bus1.setStatus("종료");
            System.out.println("변경후 -> " + bus1);
    
            // 주유량 변경
            System.out.println("\n" + "주유량 변경 (+10)");
            System.out.println("변경전 -> " + bus1);
            bus1.chgFueling(10);
            System.out.println("변경후 -> " + bus1);
    
            // 상태 변경
            System.out.println("\n" + "운행중으로 변경");
            System.out.println("변경전 -> " + bus1);
            bus1.setStatus("운행");
            System.out.println("변경후 -> " + bus1);
    
            // 승객 탑승 - 최대 승객 수 이하일때만 가능
            System.out.println("\n" + "승객 45명 탑승");
            System.out.println("변경전 -> " + bus1);
            bus1.addCur_passenger(45);
            System.out.println("변경후 -> " + bus1);
    
            // 승객 탑승
            System.out.println("\n" + "승객 5명 탑승");
            System.out.println("변경전 -> " + bus1);
            bus1.addCur_passenger(5);
            System.out.println("변경후 -> " + bus1);
    
            // 주유량 변경
            System.out.println("\n" + "주유량 변경 (-55)");
            System.out.println("변경전 -> " + bus1);
            bus1.chgFueling(-55);
            System.out.println("변경후 -> " + bus1);
    
            // 속도 변경 - 주유량 10 이상일때만 가능
            System.out.println("\n" + "속도 변경 (+10)");
            System.out.println("변경전 -> " + bus1);
            bus1.chgSpeed(10);
            System.out.println("변경후 -> " + bus1);
    
            // 주유량 변경
            System.out.println("\n" + "주유량 변경 (+10)");
            System.out.println("변경전 -> " + bus1);
            bus1.chgFueling(10);
            System.out.println("변경후 -> " + bus1);
    
            // 속도 변경
            System.out.println("\n" + "속도 변경 (+10)");
            System.out.println("변경전 -> " + bus1);
            bus1.chgSpeed(10);
            System.out.println("변경후 -> " + bus1);
    
            // 승객 탑승 - 운행중에만 가능
            System.out.println("\n" + "승객 탑승 (+3)");
            System.out.println("변경전 -> " + bus1);
            bus1.addCur_passenger(3);
            System.out.println("변경후 -> " + bus1);
        }
    }
  • 출력

    <버스 생성>
    1번 버스가 생성되었습니다.
    2번 버스가 생성되었습니다.
    (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 0, 요금 : 0, 주유량 : 100, 현재 속도 : 50, 상태 : 운행)
    (버스 번호 : 2, 최대 승객 수 : 30, 현재 승객 수 : 0, 요금 : 0, 주유량 : 100, 현재 속도 : 50, 상태 : 운행)
    
    승객 2명 추가
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 0, 요금 : 0, 주유량 : 100, 현재 속도 : 50, 상태 : 운행)
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 100, 현재 속도 : 50, 상태 : 운행)
    
    주유량 변경 (-50)
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 100, 현재 속도 : 50, 상태 : 운행)
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 50, 현재 속도 : 50, 상태 : 운행)
    
    차고지행으로 변경
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 50, 현재 속도 : 50, 상태 : 운행)
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 50, 현재 속도 : 50, 상태 : 차고지행)
    
    주유량 변경 (+10)
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 50, 현재 속도 : 50, 상태 : 차고지행)
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 60, 현재 속도 : 50, 상태 : 차고지행)
    
    운행중으로 변경
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 60, 현재 속도 : 50, 상태 : 차고지행)
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 60, 현재 속도 : 50, 상태 : 운행)
    
    승객 45명 탑승
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 60, 현재 속도 : 50, 상태 : 운행)
    최대 승객 수는 30명 입니다. 현재 승객 수는 최대 승객 수를 초과할 수 없습니다.
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 60, 현재 속도 : 50, 상태 : 운행)
    
    승객 5명 탑승
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 2, 요금 : 2000, 주유량 : 60, 현재 속도 : 50, 상태 : 운행)
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 60, 현재 속도 : 50, 상태 : 운행)
    
    주유량 변경 (-55)
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 60, 현재 속도 : 50, 상태 : 운행)
    주유가 필요합니다.
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 5, 현재 속도 : 50, 상태 : 차고지행)
    
    속도 변경 (+10)
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 5, 현재 속도 : 50, 상태 : 차고지행)
    주유량을 확인해주세요.
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 5, 현재 속도 : 50, 상태 : 차고지행)
    
    주유량 변경 (+10)
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 5, 현재 속도 : 50, 상태 : 차고지행)
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 15, 현재 속도 : 50, 상태 : 차고지행)
    
    속도 변경 (+10)
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 15, 현재 속도 : 50, 상태 : 차고지행)
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 15, 현재 속도 : 60, 상태 : 차고지행)
    
    승객 탑승 (+3)
    변경전 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 15, 현재 속도 : 60, 상태 : 차고지행)
    현재 버스 상태는 차고지행 입니다. 버스가 운행중일 때만 승객이 탑승할 수 있습니다.
    변경후 -> (버스 번호 : 1, 최대 승객 수 : 30, 현재 승객 수 : 7, 요금 : 7000, 주유량 : 15, 현재 속도 : 60, 상태 : 차고지행)

step2. Taxi class 만들기

1. 변수설정

private final int taxi_num;  // 택시 번호
private int max_passenger;  // 최대 승객 수
private int cur_passenger;  // 현재 승객 수
private int fueling;        // 주유량
private int speed;          // 속도
private String destination; // 목적지
private int basic_dist;     // 기본 거리
private int dest_dist;      // 목적지까지 거리
private int basic_fee;      // 기본 요금
private int dist_fee;       // 거리당 요금
private int psg_fee;        // 승객이 내야할 금액
private int total_fee;      // 택시가 번 요금 총액(누적요금)
private String status;      // 택시 상태

static int taxi_idx = 0;    // 택시 객체 생성 시, 택시 번호를 고유번호로 생성하기 위해 필요한 클래스 변수

2. 생성자

public Taxi() {
    taxi_num = ++taxi_idx;
    max_passenger = 4;
    fueling = 100;
    speed = 0;
    basic_dist = 1;  // 기본거리 1km
    basic_fee = 3000;   // 기본요금 3000원
    dist_fee = 1000;     // 거리당 요금 1km 1000원
    status = "일반";

    System.out.println(taxi_num + "번 택시가 생성되었습니다.");
}
  • 택시 번호는 고유값으로 생성
  • 초기값 - 최대 승객 수 4, 주유량 100, 속도 0, 기본거리 1(km), 기본요금 3000(원), 거리당 요금(1000원)상태 "일반"

3. 메서드

  • void initTaxi() : 승객이 내린 후 택시 상태를 초기화한다.
  • String toString() : 택시 객체 상태를 쉽게 보기 위해 toString() 메서드를 오버라이딩했다.
  • void chgFueling(int fueling) : 주유량을 입력받은만큼 넣어주고 10 미만인지 체크한다.
  • void checkFueling() : 운행 전 주유량이 10 미만이라면 메시지를 출력하고 "운행불가" 상태로 만든다.
  • void setDestination(String destination) : 목적지를 넣어준다.
  • setDest_dist(int dest_dist) : 목적지까지의 거리를 넣어주고, 승객이 지불할 요금을 계산하는 함수(calculatePsg_fee)를 호출한다.
  • void addCur_passenger(int passenger) : 입력받은 passenger 수만큼 현재 승객 수를 업데이트한다. 단, 최대 승객 수를 넘지 않아야 하고 일반 상태의 택시여야 한다.
  • void chgSpeed(int speed) : 속도를 입력받아 현재 속도를 업데이트한다.
  • void calculatePsg_fee() : 승객이 내야할 요금을 계산한다.
  • void payFee() : 승객이 요금을 지불한 후, 택시의 누적금액을 더해준다. 지불이 끝난 택시의 상태를 초기화해준다.

4. 코드

  • class Taxi

    public class Taxi {
        private final int taxi_num;
        private int max_passenger;
        private int cur_passenger;
        private int fueling;
        private int speed;
        private String destination;
        private int basic_dist;
        private int dest_dist;
        private int basic_fee;
        private int dist_fee;
        private int psg_fee;    // 승객이 내야할 금액
        private int total_fee;  // 택시가 번 요금 총액(누적요금)
        private String status;
    
        static int taxi_idx = 0;
    
        public Taxi() {
            taxi_num = ++taxi_idx;
            max_passenger = 4;
            fueling = 100;
            speed = 0;
            basic_dist = 1;  // 기본거리 1km
            basic_fee = 3000;   // 기본요금 3000원
            dist_fee = 1000;     // 거리당 요금 1km 1000원
            status = "일반";
    
            System.out.println(taxi_num + "번 택시가 생성되었습니다.");
        }
    
        public void initTaxi() {
            cur_passenger = 0;
            destination = "";
            dest_dist = 0;
            psg_fee = 0;
            status = "일반";
            checkFueling();
        }
    
        public String toString() {
            return "(택시 번호 : " + taxi_num +
                    ", 최대 승객 수 : " + max_passenger + ", 현재 승객 수 : " + cur_passenger +
                    ", 기본 요금 : " + basic_fee + ", 목적지 : " + destination + ", 목적지까지 거리 : " + dest_dist + "km" +
                    ", 지불할 요금 : " + psg_fee + ", 주유량 : " + fueling + ", 현재 속도 : " + speed + ", 상태 : " + status +
                    ", 누적 요금 : " + total_fee +")";
        }
    
        public void chgFueling(int fueling) {
            this.fueling += fueling;
            checkFueling();
        }
    
        public void checkFueling() {
            if (this.fueling < 10 && !status.equals("운행")) {
                System.out.println("주유가 필요합니다.");
                this.status = "운행불가";
            }
        }
    
        public void setDestination(String destination) {
            this.destination = destination;
        }
    
        public void setDest_dist(int dest_dist) {
            this.dest_dist = dest_dist;
            calculatePsg_fee(); // 승객이 지불할 요금 계산
        }
    
        public void addCur_passenger(int passenger) {
            if (this.cur_passenger + passenger > this.max_passenger)
                System.out.println("최대 승객 수는 " + this.max_passenger
                        + "명 입니다. 탑승 승객 수는 잔여 승객 수인 " + (max_passenger - cur_passenger) + "명을 초과할 수 없습니다.");
    
            if (!this.status.equals("일반"))
                System.out.println("현재 택시 상태는 " + this.status + " 입니다. 택시가 일반 상태인 경우에만 승객이 탑승할 수 있습니다.");
    
            if (this.cur_passenger + passenger <= this.max_passenger && this.status.equals("일반")) {
                this.cur_passenger += passenger;
                status = "운행중";
            }
        }
    
        public void chgSpeed(int speed) {
            this.speed += speed;
        }
    
        public void calculatePsg_fee() {
            psg_fee = (dest_dist <= basic_dist) ? basic_fee : basic_fee + (dest_dist - basic_dist) * dist_fee;
        }
    
        public void payFee() {
            total_fee += psg_fee;
            initTaxi();
        }
    }
  • Main

    public class Main {
        public static void main(String[] args) {
            // 택시 번호 자동 생성
            System.out.println("<택시 생성>");
            Taxi taxi1 = new Taxi();
            Taxi taxi2 = new Taxi();
    
            System.out.println(taxi1);
            System.out.println(taxi2);
    
            // 승객 탑승
            System.out.println("\n" + "승객 2명 탑승, 목적지 : 서울역, 거리 : 2(km)");
            System.out.println("변경전 -> " + taxi1);
            taxi1.addCur_passenger(2);
            taxi1.setDestination("서울역");
            taxi1.setDest_dist(2);
            System.out.println("변경후 -> " + taxi1);
    
            // 속도 변경
            System.out.println("\n" + "속도 변경 (+60)");
            System.out.println("변경전 -> " + taxi1);
            taxi1.chgSpeed(60);
            System.out.println("변경후 -> " + taxi1);
    
            // 주유량 변경
            System.out.println("\n" + "주유량 변경 (-80)");
            System.out.println("변경전 -> " + taxi1);
            taxi1.chgFueling(-80);
            System.out.println("변경후 -> " + taxi1);
    
            // 요금 결제
            System.out.println("\n" + "요금 결제");
            System.out.println("변경전 -> " + taxi1);
            taxi1.payFee();
            System.out.println("변경후 -> " + taxi1);
    
            // 승객 탑승 -> 5명 탑승 불가
            System.out.println("\n" + "승객 5명 탑승");
            System.out.println("변경전 -> " + taxi1);
            taxi1.addCur_passenger(5);
            System.out.println("변경후 -> " + taxi1);
    
            // 승객 탑승
            System.out.println("\n" + "승객 2명 탑승, 목적지 : 서울역, 거리 : 2(km)");
            System.out.println("변경전 -> " + taxi1);
            taxi1.addCur_passenger(3);
            taxi1.setDestination("구로디지털단지역");
            taxi1.setDest_dist(12);
            System.out.println("변경후 -> " + taxi1);
    
            // 주유량 변경
            System.out.println("\n" + "주유량 변경 (-20)");
            System.out.println("변경전 -> " + taxi1);
            taxi1.chgFueling(-20);
            System.out.println("변경후 -> " + taxi1);
    
            // 요금 결제
            System.out.println("\n" + "요금 결제");
            System.out.println("변경전 -> " + taxi1);
            taxi1.payFee();
            System.out.println("변경후 -> " + taxi1);
        }
    
    }
  • 출력

    <택시 생성>
    1번 택시가 생성되었습니다.
    2번 택시가 생성되었습니다.
    (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 0, 기본 요금 : 3000, 목적지 : null, 목적지까지 거리 : 0km, 지불할 요금 : 0, 주유량 : 100, 현재 속도 : 0, 상태 : 일반, 누적 요금 : 0)
    (택시 번호 : 2, 최대 승객 수 : 4, 현재 승객 수 : 0, 기본 요금 : 3000, 목적지 : null, 목적지까지 거리 : 0km, 지불할 요금 : 0, 주유량 : 100, 현재 속도 : 0, 상태 : 일반, 누적 요금 : 0)
    
    승객 2명 탑승, 목적지 : 서울역, 거리 : 2(km)
    변경전 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 0, 기본 요금 : 3000, 목적지 : null, 목적지까지 거리 : 0km, 지불할 요금 : 0, 주유량 : 100, 현재 속도 : 0, 상태 : 일반, 누적 요금 : 0)
    변경후 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 2, 기본 요금 : 3000, 목적지 : 서울역, 목적지까지 거리 : 2km, 지불할 요금 : 4000, 주유량 : 100, 현재 속도 : 0, 상태 : 운행중, 누적 요금 : 0)
    
    속도 변경 (+60)
    변경전 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 2, 기본 요금 : 3000, 목적지 : 서울역, 목적지까지 거리 : 2km, 지불할 요금 : 4000, 주유량 : 100, 현재 속도 : 0, 상태 : 운행중, 누적 요금 : 0)
    변경후 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 2, 기본 요금 : 3000, 목적지 : 서울역, 목적지까지 거리 : 2km, 지불할 요금 : 4000, 주유량 : 100, 현재 속도 : 60, 상태 : 운행중, 누적 요금 : 0)
    
    주유량 변경 (-80)
    변경전 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 2, 기본 요금 : 3000, 목적지 : 서울역, 목적지까지 거리 : 2km, 지불할 요금 : 4000, 주유량 : 100, 현재 속도 : 60, 상태 : 운행중, 누적 요금 : 0)
    변경후 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 2, 기본 요금 : 3000, 목적지 : 서울역, 목적지까지 거리 : 2km, 지불할 요금 : 4000, 주유량 : 20, 현재 속도 : 60, 상태 : 운행중, 누적 요금 : 0)
    
    요금 결제
    변경전 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 2, 기본 요금 : 3000, 목적지 : 서울역, 목적지까지 거리 : 2km, 지불할 요금 : 4000, 주유량 : 20, 현재 속도 : 60, 상태 : 운행중, 누적 요금 : 0)
    변경후 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 0, 기본 요금 : 3000, 목적지 : , 목적지까지 거리 : 0km, 지불할 요금 : 0, 주유량 : 20, 현재 속도 : 60, 상태 : 일반, 누적 요금 : 4000)
    
    승객 5명 탑승
    변경전 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 0, 기본 요금 : 3000, 목적지 : , 목적지까지 거리 : 0km, 지불할 요금 : 0, 주유량 : 20, 현재 속도 : 60, 상태 : 일반, 누적 요금 : 4000)
    최대 승객 수는 4명 입니다. 탑승 승객 수는 잔여 승객 수인 4명을 초과할 수 없습니다.
    변경후 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 0, 기본 요금 : 3000, 목적지 : , 목적지까지 거리 : 0km, 지불할 요금 : 0, 주유량 : 20, 현재 속도 : 60, 상태 : 일반, 누적 요금 : 4000)
    
    승객 2명 탑승, 목적지 : 서울역, 거리 : 2(km)
    변경전 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 0, 기본 요금 : 3000, 목적지 : , 목적지까지 거리 : 0km, 지불할 요금 : 0, 주유량 : 20, 현재 속도 : 60, 상태 : 일반, 누적 요금 : 4000)
    변경후 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 3, 기본 요금 : 3000, 목적지 : 구로디지털단지역, 목적지까지 거리 : 12km, 지불할 요금 : 14000, 주유량 : 20, 현재 속도 : 60, 상태 : 운행중, 누적 요금 : 4000)
    
    주유량 변경 (-20)
    변경전 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 3, 기본 요금 : 3000, 목적지 : 구로디지털단지역, 목적지까지 거리 : 12km, 지불할 요금 : 14000, 주유량 : 20, 현재 속도 : 60, 상태 : 운행중, 누적 요금 : 4000)
    주유가 필요합니다.
    변경후 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 3, 기본 요금 : 3000, 목적지 : 구로디지털단지역, 목적지까지 거리 : 12km, 지불할 요금 : 14000, 주유량 : 0, 현재 속도 : 60, 상태 : 운행불가, 누적 요금 : 4000)
    
    요금 결제
    변경전 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 3, 기본 요금 : 3000, 목적지 : 구로디지털단지역, 목적지까지 거리 : 12km, 지불할 요금 : 14000, 주유량 : 0, 현재 속도 : 60, 상태 : 운행불가, 누적 요금 : 4000)
    주유가 필요합니다.
    변경후 -> (택시 번호 : 1, 최대 승객 수 : 4, 현재 승객 수 : 0, 기본 요금 : 3000, 목적지 : , 목적지까지 거리 : 0km, 지불할 요금 : 0, 주유량 : 0, 현재 속도 : 60, 상태 : 운행불가, 누적 요금 : 18000)

step3. 추상클래스 및 인터페이스 확장

Bus와 Taxi 클래스를 만들고 보니, 둘의 공통 기능이 꽤 많았다. 번호, 주유량, 속도, 승객 수 등 공통으로 가지는 변수도 많았고, 운행, 속도 변경, 상태 변경, 승객 탑승 등 공통된 기능도 있었다.

하지만, 기능이 아주 똑같지는 않고 버스와 택시 각각의 특성에 맞게 세부적인 구현 내용은 달랐다.

내용까지 똑같은 기능, 큰 틀에서의 기능은 같지만 세부 내용은 다른 기능, 각 클래스에서만 사용하는 기능들을 어떻게 잘 나눠어 정리해볼 수 있을까?

1. 추상클래스

먼저 공통으로 사용하는 속성(변수)과 세부 내용까지 모두 같은 기능들을 모아 추상 클래스로 만들어보자.

  • abstract class AbstractTransportation
    • 공통 변수 선언
      int number;     // 번호
      int maxPassenger;  // 최대 승객 수
      int curPassenger;  // 현재 승객 수
      int fueling = 100;        // 주유량
      int speed = 10;          // 현재 속도
      String status;      // 상태
      int basicFee;       // 기본 요금
      int totalFee;       // 누적 요금
      
  • 공통 메서드 선언
    • void chgFueling(int fueling) : 주유량 변경 메서드
    • void checkPassengerNum(int passenger) : 승객 수 체크 메서드
      public void chgFueling(int fueling) {
          this.fueling += fueling;
          checkFueling();
      }
      
      public void checkPassengerNum(int passenger) {
          if (curPassenger + passenger > maxPassenger)
              System.out.println("최대 승객 수는 " + maxPassenger
                      + "명 입니다. 탑승 승객 수는 잔여 승객 수인 " + (maxPassenger - curPassenger) + "명을 초과할 수 없습니다.");
      }

2. 인터페이스

기능의 큰 틀은 같지만 세부 내용은 조금 다른 기능들을 모아 인터페이스로 만들어보자.

이 때 클래스가 필요없는 기능을 구현하지 않도록 기능들을 잘 나누어 인터페이스를 만들어야 한다.

예를 들어 버스 클래스는 목적지를 설정할 필요가 없는데, 목적지를 설정하는 기능이 담긴 인터페이스를 구현하는 상황을 만들지 않아야 한다.

인터페이스 기능을 잘 나누어 클래스가 구현할 수 있도록 하자.

  • Transportation : 공통 사용
    • void checkFueling() : 연료량 10 미만인지 체크
    • void addCurPassenger(int passenger) : 승객 탑승
    • void chgSpeed(int spped) : 속도 변경
    • void payFee() : 요금 계산
  • ChangeStatusWithParameter : 상태값 변경(파라미터 있는 경우)
    • void chgStatus(String status)
  • ChangeStatusWithNoParameter : 상태값 변경(파라미터 없는 경우)
    • void chgStatus()
  • setDestination : 목적지 세팅
    • void setDestination(String destination) : 목적지 설정
    • void setDestDist(int DestDist) : 목적지까지 거리 설정

3. 구현

공통 기능을 모아둔 인터페이스를 추상 클래스가 구현하고, 버스와 택시 클래스는 그 추상 클래스를 상속받는다. 공통 기능은 추상 클래스와 인터페이스에서 구현해주고, 각자 구현해야하는 기능들도 오버라이딩을 통해 재정의 해주면 되니 코드가 처음보다 많이 깔끔해진다.

  • abstract class AbstractTransportation implements Transportation
  • class Bus extends AbstractTransportation implements ChangeStatusWithParameter
  • class Taxi extends AbstractTransportation implements ChangeStatusWithNoParameter, SetDestination

4. 코드

  • [abstracnt class] AbstractTransportation
    public abstract class AbstractTransportation implements Transportation {
        int number;     // 번호
        int maxPassenger;  // 최대 승객 수
        int curPassenger;  // 현재 승객 수
        int fueling = 100;        // 주유량
        int speed = 10;          // 현재 속도
        String status;      // 상태
        int basicFee;       // 기본 요금
        int totalFee;       // 누적 요금
    
        public void chgFueling(int fueling) {
            this.fueling += fueling;
            checkFueling();
        }
    
        public void checkPassengerNum(int passenger) {
            if (curPassenger + passenger > maxPassenger)
                System.out.println("최대 승객 수는 " + maxPassenger
                        + "명 입니다. 탑승 승객 수는 잔여 승객 수인 " + (maxPassenger - curPassenger) + "명을 초과할 수 없습니다.");
        }
    
    }
  • [interface] Transportation
    public interface Transportation {
        void checkFueling();
        void addCurPassenger(int passenger);
        void chgSpeed(int speed);
        void payFee();
    }
  • ChangeStatusWithParameter
    public interface ChangeStatusWithParameter {
        void chgStatus(String status);
    }
  • ChangeStatusWithNoParameter
    public interface ChangeStatusWithNoParameter {
        void chgStatus();
    }
  • SetDestination
    public interface SetDestination {
        void setDestination(String destination);
        void setDestDist(int DestDist);
    }
  • Bus
    public class Bus extends AbstractTransportation implements ChangeStatusWithParameter {
    
        static int bus_idx = 0;
    
        public Bus() {
            number = ++bus_idx;
            maxPassenger = 30;
            status = "운행";
    
            System.out.println(number + "번 버스가 생성되었습니다.");
        }
    
        public String toString() {
            return "(버스 번호 : " + number +
                    ", 최대 승객 수 : " + maxPassenger + ", 현재 승객 수 : " + curPassenger +
                    ", 요금 : " + totalFee + ", 주유량 : " + fueling +
                    ", 현재 속도 : " + speed + ", 상태 : " + status + ")";
        }
    
        @Override
        public void checkFueling() {
            if (fueling < 10) {
                System.out.println("주유가 필요합니다.");
                status = "차고지행";
            }
        }
    
        @Override
        public void addCurPassenger(int passenger) {
            checkPassengerNum(passenger);
    
            if (!status.equals("운행"))
                System.out.println("현재 상태는 " + status + " 입니다. 운행중일 때만 승객이 탑승할 수 있습니다.");
    
            if (curPassenger + passenger <= maxPassenger && status.equals("운행"))
                curPassenger += passenger;
    
            payFee();
        }
    
        @Override
        public void chgSpeed(int speed) {
            if (fueling < 10)
                System.out.println("주유량을 확인해주세요.");
            else
                this.speed += speed;
        }
    
        @Override
        public void payFee() {
            totalFee = basicFee * curPassenger;
        }
    
        @Override
        public void chgStatus(String status) {
            if (status.equals("종료")) {
                this.status = "차고지행";
            } else if (status.equals("운행")) {
                this.status = status;
            } else {
                System.out.println("운행 / 종료 두 상태로 변경할 수 있습니다.");
            }
            checkFueling();
        }
    }
  • Taxi
    public class Taxi extends AbstractTransportation implements ChangeStatusWithNoParameter, SetDestination {
        private String destination;
        private int basicDist;
        private int destDist;
        private int distFee;
        private int psgFee;    // 승객이 내야할 금액
    
        static int taxi_idx = 0;
    
        public Taxi() {
            number = ++taxi_idx;
            maxPassenger = 4;
            basicDist = 1;  // 기본거리 1km
            basicFee = 3000;   // 기본요금 3000원
            distFee = 1000;     // 거리당 요금 1km 1000원
            status = "일반";
    
            System.out.println(number + "번 택시가 생성되었습니다.");
        }
    
        public String toString() {
            return "(택시 번호 : " + number +
                    ", 최대 승객 수 : " + maxPassenger + ", 현재 승객 수 : " + curPassenger +
                    ", 기본 요금 : " + basicFee + ", 목적지 : " + destination + ", 목적지까지 거리 : " + destDist + "km" +
                    ", 지불할 요금 : " + psgFee + ", 주유량 : " + fueling + ", 현재 속도 : " + speed + ", 상태 : " + status +
                    ", 누적 요금 : " + totalFee +")";
        }
    
        @Override
        public void setDestination(String destination) {
            this.destination = destination;
        }
    
        @Override
        public void setDestDist(int destDist) {
            this.destDist = destDist;
            calculatePsg_fee(); // 승객이 지불할 요금 계산
        }
    
        @Override
        public void checkFueling() {
            if (this.fueling < 10 && !status.equals("운행")) {
                System.out.println("주유가 필요합니다.");
                this.status = "운행불가";
            }
        }
    
        @Override
        public void addCurPassenger(int passenger) {
            checkPassengerNum(passenger);
    
            if (!status.equals("일반"))
                System.out.println("현재 택시 상태는 " + this.status + " 입니다. 일반 상태인 경우에만 승객이 탑승할 수 있습니다.");
    
            if (this.curPassenger + passenger <= this.maxPassenger && this.status.equals("일반")) {
                this.curPassenger += passenger;
                status = "운행중";
            }
        }
    
        @Override
        public void chgSpeed(int speed) {
            this.speed += speed;
        }
    
        @Override
        public void payFee() {
            totalFee += psgFee;
            chgStatus(); // 요금 낸 후 택시 상태 변경
        }
    
        @Override
        public void chgStatus() {
            curPassenger = 0;
            destination = null;
            destDist = 0;
            psgFee = 0;
            status = "일반";
            checkFueling();
        }
    
        public void calculatePsg_fee() {
            psgFee = (destDist <= basicDist) ? basicFee : basicFee + (destDist - basicDist) * distFee;
        }
    
    }
profile
🔥

0개의 댓글