대중교통(버스, 택시)

박영준·2022년 11월 27일
0

Java

목록 보기
4/111
  • 상위 클래스는 2개의 하위 클래스의 공통된 부분을 가진다 (나뭇가지의 모양)

가이드 라인

상위 클래스 = 대중교통

  • 요소
    • 번호
    • 주유량
    • 속도
    • 속도 변경
    • 최대 승객 수
    • 있을 경우 {기타 공통 요소들}
  • 기능
    • 운행 시작
    • 속도 변경
    • 상태 변경
    • 승객 탑승
    • 있을 경우 {기타 공통 기능들}
  • 기본값
    • 주유량 = 100
    • 속도 = 0

하위 클래스 = 버스, 택시

버스

  • 요소
    • 현재 승객 수
    • 있을 경우 {기타 버스 요소들}
  • 기능
    • 있을 경우 {기타 버스 기능들}
  • 기본값
    • 최대 승객 수 = 30
    • 상태 = 운행
    • 요금 = 1000

택시

  • 요소
    • 목적지
    • 목적지까지 거리
    • 기본 거리
    • 기본 요금
    • 거리당 요금
    • 있을 경우 {기타 택시 요소들}
  • 기능
    • 거리당 요금 추가
    • 요금 결제
    • 있을 경우 {기타 택시 기능들}
  • 기본값
    • 최대 승객 수 = 4
    • 기본 요금 = 3000
    • 거리당 요금 = 1000
    • 기본 거리 = 1
    • 주유량 = 100
    • 상태 = 일반
    • 속도 = 0

시나리오

Bus

  1. Bus - 2대 생성
  2. 출력 Bus
    1. 각 Bus 번호 다른지 확인

※ Bus 1대로 진행

  1. Bus
    1. 승객 +2
  2. 출력 Bus
    1. 탑승 승객 수 = 2
    2. 잔여 승객 수 = 28
    3. 요금 확인 = 2000
  3. Bus
    1. 주유량 -50
  4. 출력 Bus
    1. 주유량 = 50
  5. Bus - 상태 변경
    1. 차고지행
  6. Bus
    1. 주유량 +10
  7. 출력 Bus
    1. 상태 = 차고지행
    2. 주유량 = 60
  8. Bus - 상태 변경
    1. 운행중
  9. Bus
    1. 승객 +45
  10. 알럿 ‘최대 승객 수 초과’
  11. Bus
    1. 승객 +5
  12. 출력 Bus
    1. 탑승 승객 수 = 5
    2. 잔여 승객 수 = 25
    3. 요금 = 5000
  13. Bus
    1. 주유량 -55
  14. 출력 Bus
    1. 주유량 = 5
    2. 상태 = 차고지행
  15. 알럿 ‘주유 필요’

Taxi

  1. Taxi - 2대 생성
  2. 출력 Taxi
    1. 각 Taxi 번호 다른지 확인
    2. 주유량 = 100
    3. 상태 = 일반

※ Taxi 1대로 진행

  1. Taxi
    1. 승객 +2
    2. 목적지 = 서울역
    3. 목적지까지 거리 = 2km
  2. 출력 Taxi
    1. 탑승 승객 수 = 2
    2. 잔여 승객 수 = 2
    3. 기본 요금 확인 = 3000
    4. 목적지 = 서울역
    5. 목적지까지 거리 = 2km
    6. 지불할 요금 = 4000
    7. 상태 = 운행중
  3. Taxi
    1. 주유량 -80
  4. Taxi - 요금 결제
  5. 출력 Taxi
    1. 주유량 = 20
    2. 누적 요금 = 4000
  6. Taxi
    1. 승객 +5
  7. 알럿 ‘최대 승객 수 초과’
  8. Taxi
    1. 승객 +3
    2. 목적지 = 구로디지털단지역
    3. 목적지까지 거리 = 12km
  9. 출력 Taxi
    1. 탑승 승객 수 = 3
    2. 잔여 승객 수 = 1
    3. 기본 요금 확인 = 3000
    4. 목적지 = 구로디지털단지역
    5. 목적지까지 거리 = 12km
    6. 지불할 요금 = 14000
  10. Taxi
    1. 주유량 -20
  11. Taxi - 요금 결제
  12. 출력 Taxi
    1. 주유량 = 0
    2. 상태 = 운행불가
    3. 누적 요금 = 18000원
  13. 알럿 ‘주유 필요’

상위 클래스 Transportation

package test1;
//상위 클래스 Transportation --> 추상 클래스
public abstract class Transportation {

    //필드
    int num;    //번호
    int currentGas; //주유량
    int acceleration;   //속도
    String status;  //상태
    
    //메소드 --> 추상 메소드: 앞에 abstract 을 붙임

    //추상 메소드
    abstract void board(int pass, String dest, int dis);    //탑승 board: 메소드 명,      승객 int pass, 목적지 String dest, 거리 int dis: 매개변수

    abstract void refuel();     //주유량 refuel: 메소드 명

    //문법: abstract 반환타입 메소드이름();
    abstract int refuel(int gas);       //refuel: 메소드 명 int gas: 필드명

    abstract int board(int pass);       //board: 메소드 명, int pass: 필드명

}
  • 추상 클래스
    - 추상화 : 클래스간의 공통점을 찾아내서 공통의 부모를 설계하는 작업
    - 추상클래스는 객체생성이 불가능
    - 추상 클래스 장점
    • 부모클래스에서 공통 부분을 구현과 설계가 완료되면 자식 클래스에서 상속받아 기능을 확장 시 이롭다.
    • 자식 클래스에서 추상메서드를 반드시 구현하도록 강요한다. 이는 프로그램의 표준화 정도를 높인다. --> 구현하지 않으면 오류가 발생
    • 공통 사항이 한곳에서 관리되어 개발 및 유지보수에 용이
  • 추상 메소드
    - 여러 객체에 공통되는 사항을 추출하여 부모 클래스에 선언 및 구현을 하고, 자식 클래스에는 특징을 구현하여 기능을 확장
    - 추상 메서드 선언 이유? 설계자가 특정 메서드를 각 클래스 별로 재 구현을 원하지만 부모 클래스에서 일반 메서드로 구현하면 자식 클래스에서 구현을 하지 않는 경우가 발생할 수 있다.
    - 이런 메서드를 추상 메서드로 선언하면 자식 클래스는 재구현을 강요받는다.

추상화 vs 상속

  • 추상화: 클래스간의 공통점을 찾아내서 공통의 부모를 설계 --> 상향, 하향 상관없음
  • 상속: 상위 클래스를 사용하여, 하위 클래스를 정의 --> 하향

하위 클래스 Bus

package test1;

import static test1.Taxi.gasLeft;

//클래스
public class Bus extends Transportation {

    //필드
    int maxPass = 30;		// 최대 승객 수
    int currentPass = 0;	// 현재 승객 수
    int cost = 1000;		// 요금
    
    //생성자

    //버스 번호 지정 [고유값으로 생성되어야 되기에 랜덤함수로 함]
    //버스 번호 (고유값으로 생성되어야 되기에 랜덤함수로. 랜덤함수는 기본형이 Double 형이기에 (int)로 정수화. 1부터 값을 뽑고 싶다면 +1 => 랜덤 함수는 0부터 나오기 때문에)
    public Bus() {          //Bus(): 기본 생성자, num: 상위 클래스의 필드명
        this.num = (int) (Math.random() * 100 + 1);     //Math.random() 메소드: 0.0 ~ 1에 무한히 가까운 수를 제공
        System.out.println("버스 번호: " + num);
    }

    //메소드

    // 버스 상태 변경
    boolean busStatus(boolean change) {     //boolean 메소드, busStatus: 메소드 명, boolean change: 매개변수(메소드 실행 시, 필요한 데이터를 받기 위한 변수)
        if(change == true)          //매개변수 chage가 true라면, 상위 클래스의 필드 status는 "운행중"이 된다
            status = "운행중";
        else {                      // 매개변수 chage가 false라면, 상위 클래스의 필드 status는 "차고지행", 하위 클래스의 필드 currentPass은 0, 하위 클래스의 필드 maxPass은 30이 된다
            status = "차고지행";
            currentPass = 0;
            maxPass = 30;
        }
        return change;
    }

    // 버스 현재 상태
    void currentBus() {     //void 메소드, currentBus: 메소드 명    --> void 이므로, 리턴값은 없음
        System.out.println("상태 = " + status);       //상위 클래스의 필드 status 를 출력
        System.out.println("주유량 = " + currentGas);      //상위 클래스의 필드 currentGas 를 출력
    }

    //오버라이드
    @Override
    void board(int pass, String dest, int dis) {        //상위 클래스에서 봤던 int pass, String dest, int dis 매개변수를 여기 하위 클래스에서 재정의(오버라이드)

    }

    // 주유량
    @Override
    void refuel() {     //refuel: 상위 클래스의 메소드 명
    }

    int refuel(int gas) {       //refuel: 상위 클래스의 메소드 명, int gas: 매개변수
        currentGas += gas;          //매개 변수 gas 를 더해서 상위 클래스의 필드 currentGas 로 값을 저장한다
        if(!gasLeft()) {        //import 에서 gasLeft 가 있기 때문에, gasLeft() 로 사용 가능. --> 하위 클래스 Taxi 에서 static boolean gasLeft()를 사용하므로
            status = "차고지행";    //gasLeft가 아니라면, 상위 클래스의 status는 "차고지행"
        }
        return currentGas;
    }

    boolean available() {       //available: 메소드 명 --> 이렇게 하위 클래스에서 새롭게 선언하기도 함
        //승객 탑승은 ‘최대 승객수’ 이하까지 가능
        return maxPass >= currentPass;      //maxPass:하위 클래스의 필드, currentPass: 하위 클래스의 필드 --> 최대 승객 수는 현재 승객 수 보다 크거나 같다
    }

    // 승객 탑승
    @Override
    int board(int pass) {       //board: 상위 클래스의 메소드 명, pass: 매개변수
        if(pass >= (maxPass-currentPass))       //maxPass:하위 클래스의 필드, currentPass: 하위 클래스의 필드 --> 승객 수는 최대 승객 수에서 현재 승객 수를 뺀 수 보다 크거나 같다면
            System.out.println("최대 승객 수 초과");   //"최대 승객 수 초과"를 출력

        else {          // 아니라면
            if(available()) {       //available: 하위 클래스 Bus 의 메소드 명 --> available 가 true 라면,
                currentPass += pass;        // 하위 클래스의 필드 currentPass 에 승객 수 pass 를 더하고, 탑승 승객 수, 잔여 승객 수, 요금 확인 을 출력한다
                System.out.println("탑승 승객 수 = "+pass+"명");
                System.out.println("잔여 승객 수 = "+(maxPass-currentPass)+"명");
                System.out.println("요금 확인 = "+(cost*pass));
            }
            if(!available()) {      //available 가 false 라면,
                System.out.println("최대 승객 수 초과");       //최대 승객 수 초과 를 출력한다
            }
        }
        return currentPass;
    }
}
  • import static test1.Taxi.gasLeft;
    - 클래스 내의 모든 정적(static) 메소드를 import 하기 위함
    - import 란? 다른 패키지 안의 클래스를 사용하기 위해서
    - import static 란? 일반적인 import 와 달리, 해당 클래스의 정적 변수나 메소드를 패키지명과 클래스명 없이 약칭으로 쓸 수 있도록 해준다.
    - gasLeft는 Taxi 클래스의 static 의 메소드명 이다. --> 이렇게 사용되면, gasLeft() 메서드는 클래스명 없이 사용 가능해짐
    - 전역참조 문자인 *를 사용해서 모두 불러오기도 함

  • 오버라이드
    - 부모 클래스에 있는 메소드를 자식 클래스에서 재정의 하는 것
    - 부모 클래스의 메소드를 자식클래스에서 동일한 이름으로 다시 재정의 하면, 부모클래의 메소드를 찾지 않고 자식 클래스의 메소드를 호출

import java.util.*;

  • 한 패키지에서 여러 클래스를 사용하는 경우 클래스의 이름을 일일이 지정해주는 것보다 '패키지명.*'과 같이 하는 것이 편리
  • 단점: 하지만, import하는 패키지의 수가 많을 때는 어느 클래스가 어느 패키지에 속하는지 구별하기 어렵다

하위 클래스 testBus

package test1;

public class testBus {

    public static void main(String[] args) {
        // 버스 테스트
        // 1번
        // 1~2. 버스 2대 생성 & 출력 --> 랜덤 번호 출력
        Bus bus1 = new Bus();       //객체 생성
        Bus bus2 = new Bus();

        // 2번 (버스 1대로 진행)
        // 1 ~ 2. 승객 +2 & 출력    //하위 클래스 Bus 의 int board 메소드 실행 --> if/else 中 else 문 실행 --> available()/!available() 中 true 인 available() 실행(available()메소드로 가서 true 인지 판단)
        bus1.board(2);          // --> 하위 클래스의 필드 currentPass 에 승객 수 pass 를 더하고, 탑승 승객 수, 잔여 승객 수, 요금 확인 을 출력
        // 3 ~ 4. 주유량 50        //하위 클래스 Bus 의 int refuel 메소드 실행 --> 매개 변수 gas 를 더해서 상위 클래스의 필드 currentGas 로 값을 저장
        bus1.refuel(50);        //--> return currentGas
        System.out.println("주유량 = "+bus1.currentGas);
        // 5. 상태 변경 => 차고지행         //하위 클래스 Bus 의 boolean busStatus 메소드 실행 --> 매개변수 boolean change 가 false라면, status = "차고지행"
        bus1.busStatus(false);         // --> return change
        // 6. 주유량 +10           //하위 클래스 Bus 의 int refuel 메소드 실행
        bus1.refuel(10);
        // 7. 버스 상태와 주유량 출력
        bus1.currentBus();
        // 8. 상태 변경 => 운행중
        bus1.busStatus(true);
        // 9 ~ 10. 승객 +45 => 최대 승객 수 초과         //하위 클래스 Bus 의 int board 메소드 실행
        bus1.board(45);
        // 11 ~ 12. 승객 +5 & 출력      //하위 클래스 Bus 의 int board 메소드 실행
        bus1.board(5);
        // 13. 주유량 -55          //하위 클래스 Bus 의 int refuel 메소드 실행 --> if(!gasLeft()) 이면, status = "차고지행"
        bus1.refuel(-55);
        // 14. 버스 상태와 주유량 출력        //하위 클래스 Bus 의 currentBus 메소드 실행 --> "상태 = " + status 와 "주유량 = " + currentGas 출력
        bus1.currentBus();
    }
}
  • public static void main(String[] args)
    - public: 접근 제어자
    - static: 정적 함수 임을 나타냄
    - void: 리턴값이 없는 메소드
    - String: 문자열
    - [ ]: 배열
    - args: 변수명(다른 변수명으로 해도 무방)

하위 클래스 Taxi

package test1;

public class Taxi extends Transportation {

    //필드

    String destination;			// 목적지
    int distance;				// 목적지까지 거리
    int maxPass = 4;			// 최대 승객수
    int defaultDistance = 1;	// 기본 거리
    int defaultCost = 3000;		// 기본 요금
    int perDistance = 1000;		// 거리당 요금

    //정적(static) 필드(변수)
    static String status = "일반";		// 상태
    int speed = 0;				// 속도
    int total = 0;				// 누적 금액
    int cost;					// 승객이 지불할 금액


    //생성자

    // 택시 번호 지정 [고유값으로 생성되어야 되기에 랜덤함수로 함]
    public Taxi() {
        this.num = (int)(Math.random()*100+1);
        // 랜덤함수는 기본형이 Double 형이기에 (int)로 정수화
        // 1부터 값을 뽑고 싶다면 +1 => 랜덤 함수는 0부터 나오기 때문에
        System.out.println("택시 번호 : "+num);
        Taxi.drive();
    }
    
    //static 메소드: 메소드 명 앞에 static 을 붙임 --> 객체 생성없이 클래스를 통해 메서드를 직접 호출 가능
    static boolean drive() {
        if (!gasLeft()) {
            status = "운행 불가";
            System.out.println("주유 필요");
            return false;
        }
        return true;
    }

    static boolean gasLeft() {
        return false;
    }

    // 탑승		승객			목적지		거리
    @Override
    void board(int pass, String dest, int dis) {         //상위 클래스에서 봤던 int pass, String dest, int dis 매개변수를 여기 하위 클래스에서 재정의(오버라이드)
        if(status == "일반") {
            if(pass > 4)
                System.out.println("최대 승객 수 초과");

            else {
                if(dis==1)
                    cost = defaultCost+ (perDistance*dis);
                else
                    cost = defaultCost+ (perDistance*(dis-1));
                status = "운행중";
                System.out.println("탑승 승객 수 = "+pass);
                System.out.println("잔여 승객 수 = "+ (maxPass-pass));
                System.out.println("기본 요금 확인 = "+defaultCost);
                System.out.println("목적지 = "+dest);
                System.out.println("목적지까지 거리 = "+ dis+"km");
                System.out.println("지불할 요금 = "+cost);
                total += cost;

            }
        }
    }

    @Override
    void refuel() {
    }
    // 주유량
    @Override
    int refuel(int gas) {
        currentGas += gas;
        if(!gasLeft()) {
            status = "운행 불가";
        }
        return currentGas;
    }

    @Override
    int board(int pass) {
        return 0;
    }

    // 요금 지불
    int pay() {
        status = "일반";
        maxPass = 4;
        System.out.println("누적 요금 = "+ total);

        if(!gasLeft())
            System.out.println("주유 필요");

        cost = 0;
        return total;
    }

    void passenger(int pass) {
        if(pass > 4)
            System.out.println("최대 승객 수 초과");
    }
    // 속도변경
    int changeSpeed(int acceleration) {
        //주유 상태를 체크하고 주유량이 10 이상이어야 운행할 수 있음
        if(gasLeft()) {
            this.acceleration = acceleration;

            speed += acceleration;

            System.out.println("현재 속도는 "+ speed+"입니다.");

        }
        System.out.println("주유량을 확인해주세요."+currentGas);
        return currentGas;
    }
}
  • 정적(static) 필드, 변수
    - 사용 이유?
    • 변수에 static 키워드를 붙이면 자바는 메모리 할당을 딱 한번만 하게 되어 메모리 사용에 이점
    • 공유 개념: Counter c1 = new Counter(); 와 Counter c2 = new Counter(); 는 서로 다른 메모리를 가지는 독립적인 값을 가진다. 그러나, int count = 0;에서 static int count = 0; 로 static을 붙인다면, count 값이 공유되어 count값이 증가된다. (참고: https://wikidocs.net/228)

하위 클래스 testTaxi

package test1;

public class testTaxi {

    // 정적(static) 메소드
    public static void main(String[] args) {
        //TODO Auto-generated method stub

        // 택시 테스트
        // 1번
        // 1~2. 버스 2대 생성 & 출력
        Taxi taxi1 = new Taxi();        //객체 생성
        Taxi taxi2 = new Taxi();
        System.out.println("taxi1 주유량 = "+taxi1.currentGas);
        System.out.println("taxi1 상태 = "+taxi1.status);
        System.out.println("taxi2 주유량 = "+taxi2.currentGas);
        System.out.println("taxi2 상태 = "+taxi2.status);

        //2번(Taxi 1대로 진행)
        // 1~2.승객+2 목적지 = 서울역 목적지까지 거리 2km & 출력
        taxi1.board(2, "서울역", 2);
        System.out.println("상태 = "+ taxi1.status);
        // 3. 주유량 -80
        taxi1.refuel(-80);
        // 4~5. 요금결제 & 출력
        System.out.println("주유량 = "+taxi1.currentGas);
        taxi1.pay();
        // 6~7. 승객+5 & 최대승객수 초과
        taxi1.passenger(5);
        // 8~9. 승객+3 목적지 = 구로디지털단지역 목적지까지 거리 12km & 출력
        taxi1.board(3, "구로디지털단지역", 12);
        // 10. 주유량 -20
        taxi1.refuel(-20);
        // 11~12. 요금 결제 & 출력
        System.out.println("주유량 = "+taxi1.currentGas);
        taxi1.pay();        // 하위 클래스 Taxi 의 int pay() 메소드 실행
    }
}
profile
개발자로 거듭나기!

0개의 댓글