TIL

자바 - 객체지향 프로그래밍의 4가지 특징 (캡슐화, 추상화, 상속, 다형성)

Big Iron 2023. 2. 23. 14:45

 

섯다카드 20장을 포함하는 섯다카드 한 벌(SutdaDeck클래스)을 정의한 것이다.

  • 섯다카드 20장을 담는 SutdaCard배열을 초기화하시오.
  • 단, 섯다카드는 1부터 10까지의 숫자 가 적힌 카드가 한 쌍씩 있고, 숫자가 1, 3, 8인 경우에는 둘 중의 한 장은 광(Kwang)이 어야 한다.
  • 즉, SutdaCard의 인스턴스변수 isKwang의 값이 true이어야 한다.
--- 문제 코드 ---

class SutdaDeck {
    final int CARD_NUM = 20;
    SutdaCard[] cards = new SutdaCard[CARD_NUM];

    SutdaDeck() {
        // (구현) 배열 SutdaCard를 적절히 초기화 하시오.
        
    }
}

class SutdaCard {
    int num;
    boolean isKwang;

    SutdaCard() {
        this(1, true);
    }

    SutdaCard(int num, boolean isKwang) {
        this.num = num;
        this.isKwang = isKwang;
    }
    
    // info()대신 Object클래스의 toString()을 오버라이딩했다. 
    public String toString() {
        return num + ( isKwang ? "K":""); 
    }
}

class Exercise7_1 {
    public static void main(String args[]) {
        SutdaDeck deck = new SutdaDeck();
        
        for (int i = 0; i < deck.cards.length; i++)
            System.out.print(deck.cards[i] + ",");
    }
}
// 예상결과) 1K,2,3K,4,5,6,7,8K,9,10,1,2,3,4,5,6,7,8,9,10,
--- 정답 코드 ---
SutdaDeck() {
    // (구현) 배열 SutdaCard를 적절히 초기화 하시오.
    // 1. 카드배열 각 인덱스에 값을 넣어줘야 했기에 배열 크기 만큼 반복문 실행.
    for (int i=0; i<cards.length; i++) {
        
        // 2-1. 1~10까지 숫자 한 쌍 이기에 각 인덱스의 값을 저장할 지역변수 no를 만들고 (i%10+1)로 1~10 두번 저장.
        // 2-2. 각 인덱스 값마다 Kwang의 조건도 저장.
        int no = i%10 + 1;
        boolean Kwang = i < 10 && (no == 1 || no == 3 || no == 8);
        
        // 3. 아래 SutdaCard 클래스에서 num 과 isKwang을 매개변수로 받고 있으니 
        // cards 배열의 각 인덱스마다  for문 안에서 생성한 변수를 넘겨주며 인스턴스 생성.
        cards[i] = new SutdaCard(no, Kwang);
    }
}
  • 이번 문제를 보고 어떻게 시작해야 할지 정말 어려웠다. 카드 20장을 담는다는 지문을 보고 반복문을 써야 하는 건 아닐까 생각했지만 SutdaDeck (){}를 보고 생성자 면 this를 써야 하는 건 아닌지 이런 부분에서 많이 헷갈린 듯하다. 위의 정답을 구간마다 나눠 해석하며 조금은 이해할 수 있었다.
  1. 카드배열 각 인덱스에 값을 넣어줘야 했기에 배열 크기 만큼 반복문 실행.
  2. 1 ~ 10까지 숫자 한 쌍 이기에 각 인덱스의 값을 저장할 지역변수 no를 만들고 (i%10+1)로 1~10 두번 저장.
  3. 각 인덱스 값마다 Kwang의 조건도 저장.
  4. 아래 SutdaCard 클래스에서 num 과 isKwang을 매개변수로 받고 있으니 cards 배열의 각 인덱스마다 for문 안에서 생성한 변수를 넘겨주며 인스턴스 생성.

위의 SutdaDeck클래스에 다음에 정의된 새로운 메서드를 추가하고 테스트 하시오.

메서드명 : shuffle

  • 기능 : 배열 cards에 담긴 카드의 위치를 뒤섞는다.(Math.random()사용)
  • 반환타입 : 없음
  • 매개변수 : 없음

메서드명 : pick

  • 기능 : 배열 cards에서 지정된 위치의 SutdaCard를 반환한다.
  • 반환타입 : SutdaCard
  • 매개변수 : int index - 위치

메서드명 : pick

  • 기능 : 배열 cards에서 임의의 위치의 SutdaCard를 반환한다.(Math.random()사용)
  • 반환타입 : SutdaCard
  • 매개변수 : 없음
--- 문제 코드 ---
class SutdaDeck {
    final int CARD_NUM = 20;
    SutdaCard[] cards = new SutdaCard[CARD_NUM];

    SutdaDeck() {
         // 연습문제 7-1 의 답이므로 내용생략
    }

    void shuffle() {
         // (구현)
    }

    SutdaCard pick(int index) {
         // (구현)
    }
    
    SutdaCard pick() {
         // (구현)
    }
} // SutdaDeck

class SutdaCard { 
    int num;
    boolean isKwang;
    
    SutdaCard() { 
        this(1, true);
    }
    
    SutdaCard(int num, boolean isKwang) { 
        this.num = num;
        this.isKwang = isKwang; 
    }
    
    public String toString() {
        return num + ( isKwang ? "K":"");
    } 
}

class Exercise7_2 {
    public static void main(String args[]) {
        SutdaDeck deck = new SutdaDeck();
        
        System.out.println(deck.pick(0)); 
        System.out.println(deck.pick()); 
        deck.shuffle();
        
        for(int i=0; i < deck.cards.length;i++) 
            System.out.print(deck.cards[i]+",");
        
        System.out.println();
        System.out.println(deck.pick(0)); 
    }
}
//예상결과) 
// 1K
// 7 
// 2,6,10,1K,7,3,10,5,7,8,5,1,2,9,6,9,4,8K,4,3K, 
// 2
--- 정답 코드 ---

void shuffle () {
        // cards 배열 안에 SutdaCard객체가 들어간다.
        for (int i=0; i<cards.length; i++) {
            // 1. 반복문을 통해 랜덤 숫자를 변수j에 저장
            int j = (int)(Math.random()* cards.length);

            // 2. SutdaCard tmp 객체에 cards배열의 i번째를 할당.
            SutdaCard tmp = cards[i];

            // 3. cards배열의 i번째를 cards배열의 랜덤수j번째로 변경
            cards[i] = cards[j];

            // 4. cards배열의 랜덤수j번째에 1번에서 저장된 cards배열의 i번째를 할당.(tmp객체)
            cards[j] = tmp;
            // 5. i값이 증가하면서 배열 각 자리의 값들이 바뀐다.
        }
    }

    SutdaCard pick (int index) {
        // 바로 return을 해줘도 되지만 유효성 검사를 거쳐주는게 가장 좋다.
        if (index < 0 || index > cards.length) {
            return null;
        }
        return cards[index];
        
        // 처음에 풀었던 방식이지만 굳이 반복문을 쓸 필요가 없었다.
//        SutdaCard idx = null;
//        for (int i=0; i< cards.length; i++) {
//            idx = cards[index];
//        }
//        return idx;
    }

    SutdaCard pick () {
        // 임의의 위치를 반환하기 위해 랜덤 숫자 j생성
        int j = (int)(Math.random()*cards.length);
        
        return cards[j];
    }
  • shuffle 메서드에서 배열의 순서를 바꾸며 과정의 순서에 헷갈림이 있었다. 언제든 순서나 방향에 어려움이 있다면 어떤 동작을 하는지 파악하고 순서를 생각해 보는 것이 좋을 것 같다.
  1. 반복문을 통해 랜덤 숫자를 변수j에 저장
  2. SutdaCard tmp 객체에 cards배열의 i번째를 할당.
  3. cards배열의 i번째를 cards배열의 랜덤수j번째로 변경
  4. cards배열의 랜덤수j번째에 1번에서 저장된 cards배열의 i번째를 할당.(tmp객체)
  5. i값이 증가하면서 배열 각 자리의 값들이 바뀐다.

다음의 코드는 컴파일하면 에러가 발생한다. 그 이유를 설명하고 에러를 수정하기 위해서는 코드를 어떻게 바꾸어야 하는가?

--- 문제 코드 ---

class Product {
    int price; // 제품의 가격
    int bonusPoint; // 제품구매 시 제공하는 보너스점수

    // (구현)

    Product(int price) {
        this.price = price;
        bonusPoint = (int) (price / 10.0);
    }
}

class Tv extends Product {
    Tv() {
    }

    public String toString() {
        return "Tv";
    }
}

class Exercise7_3 {
    public static void main(String[] args) {
        Tv t = new Tv();
    }
}
--- 정답 코드 ---

class Product {
    int price; // 제품의 가격
    int bonusPoint; // 제품구매 시 제공하는 보너스점수

    // (구현)
    Product() {} // 기본생성자
    
    Product(int price) {
        this.price = price;
        bonusPoint = (int) (price / 10.0);
    }
}

class Tv extends Product {
    Tv() {
    }

    public String toString() {
        return "Tv";
    }
}

class Exercise7_3 {
    public static void main(String[] args) {
        Tv t = new Tv();
    }
}
  • 이 문제는 단순히 기본 생성자가 없었기에 오류가 발생했던 것이다.

MyTv클래스의 멤버변수 isPowerOn, channel, volume을 클래스 외부에서 접근할 수 없도록 제어자를 붙이고 대신 이 멤버변수들의 값을 어디서나 읽고 변경할 수 있도록 getter와 setter메서드를 추가하라.

--- 문제 코드 ---

class MyTv {
    private boolean isPowerOn;
    private int channel;
    private int volume;
    
    final int MAX_VOLUME = 100;
    final int MIN_VOLUME = 0;
    final int MAX_CHANNEL = 100;
    final int MIN_CHANNEL = 1;

    // (구현) 
    
}

class Exercise7_4 {
    public static void main(String args[]) {
        MyTv t = new MyTv();
        
        t.setChannel(10);
        System.out.println("CH:" + t.getChannel());
        t.setVolume(20);
        System.out.println("VOL:" + t.getVolume());
    }
}
//예상결과) CH:10 VOL:20
--- 정답 코드 ---

class MyTv {
    private boolean isPowerOn;
    private int channel;
    private int volume;
    
    final int MAX_VOLUME = 100;
    final int MIN_VOLUME = 0;
    final int MAX_CHANNEL = 100;
    final int MIN_CHANNEL = 1;

    // (구현) 
    public int setChannel (int channel) {
    // 1. 항상 유효성 검사를 먼저 하자 ! 
        if (channel > MAX_CHANNEL || channel < MIN_CHANNEL) {
            return;
        }
        this.channel = channel;
    }
    public void getChannel() {
        return channel;
    }
    
    public int setVolum (int volume) {
        if (volume > MAX_VOLUME || volume < MIN_VOLUME) {
            return;
        }
        this.volume = volume;
    }
    public void getVolume() {
        return volume;
    }
    
}

class Exercise7_4 {
    public static void main(String args[]) {
        MyTv t = new MyTv();
        
        t.setChannel(10);
        System.out.println("CH:" + t.getChannel());
        t.setVolume(20);
        System.out.println("VOL:" + t.getVolume());
    }
}
  • 처음에 set get(캡슐화)을 쓰는 이유는 외부의 해킹을 막기 위함인 줄 알았다. 하지만 set get(캡슐화)은 외부에서 직집적으로 데이터에 접근하는 것을 보호하기 위함이다.
  1. 유효성 검사.

위 문제에서 작성한 MyTv클래스에 이전 채널(previous channel)로 이동하는 기능 의 메서드를 추가해서 실행결과와 같은 결과를 얻도록 하시오.

--- 문제 코드 ---

class MyTv {
    private boolean isPowerOn;
    private int channel;
    private int volume;
    // (구현)
    private int prevChannel;

    final int MAX_VOLUME = 100;
    final int MIN_VOLUME = 0;
    final int MAX_CHANNEL = 100;
    final int MIN_CHANNEL = 1;

    public void setVolume(int volume) {
        if (volume > MAX_VOLUME || volume < MIN_VOLUME)
            return;
        this.volume = volume;
    }

    public int getVolume() {
        return volume;
    }

    public void setChannel(int channel) {
        if (channel > MAX_CHANNEL || channel < MIN_CHANNEL)
            return;

        // (구현)
        this.channel = channel;
    }

    public int getChannel() {
        return channel;
    }

    // (구현)
}

class Exercise7_5 {
    public static void main(String args[]) {
        MyTv2 t = new MyTv2();
        t.setChannel(10);
        System.out.println("CH:" + t.getChannel());
        t.setChannel(20);
        System.out.println("CH:" + t.getChannel());
        t.gotoPrevChannel();
        System.out.println("CH:" + t.getChannel());
        t.gotoPrevChannel();
        System.out.println("CH:" + t.getChannel());
    }
}

// CH:10
// CH:20
// CH:10
// CH:20
--- 정답 코드 ---

class MyTv {
    private boolean isPowerOn;
    private int channel;
    private int volume;
    // 구현
    // 이전 채널 저장 변수
    private int prevChannel;

    final int MAX_VOLUME = 100;
    final int MIN_VOLUME = 0;
    final int MAX_CHANNEL = 100;
    final int MIN_CHANNEL = 1;

    public void setVolume(int volume) {
        if (volume > MAX_VOLUME || volume < MIN_VOLUME)
            return;
        this.volume = volume;
    }

    public int getVolume() {
        return volume;
    }

    public void setChannel(int channel) {
        if (channel > MAX_CHANNEL || channel < MIN_CHANNEL)
            return;
        // (구현)
        // 2. 채널 설정하는 setChannel 메서드의 이전채널 변수에 현재채널 저장.
        prevChannel = this.channel;
        this.channel = channel;
    }

    public int getChannel() {
        return channel;
    }

    // (구현)
    public void gotoPrevChannel() {
//        prevChannel = getChannel();
        // 3. 이전 채널을 매개변수로 setChannel 메서드에 넘긴다.
        setChannel(prevChannel);
    }
}

class Exercise7_4 {
    public static void main(String args[]) {
        MyTv2 t = new MyTv2();
        t.setChannel(10);
        System.out.println("CH:" + t.getChannel());
        t.setChannel(20);
        System.out.println("CH:" + t.getChannel());
        t.gotoPrevChannel();
        System.out.println("CH:" + t.getChannel());
        t.gotoPrevChannel();
        System.out.println("CH:" + t.getChannel());
    }
}
  1. 이전 채널을 저장할 변수 선언.
  2. 현재 채널을 설정하는 setChannel 메서드의 이전채널 변수에 현재채널 저장..
  3. 새로운 메서드에서 setChannel메서드에 매개변수로 이전 채널을넘겨준다.

'TIL' 카테고리의 다른 글

Usecase - 유스케이스 다이어그램  (0) 2023.02.27
자바 - 약한 결합 및 약한 의존성  (0) 2023.02.25
자바 - 상속  (0) 2023.02.22
자바 - 객체지향  (0) 2023.02.21
Java - 조건문 그리고 반복문.  (0) 2023.02.14