간단하게 상속과 조합을 이해해보기
Java

간단하게 상속과 조합을 이해해보기

안녕하세요. 이번에는 상속과 조합에 대해서 고민을 해보려고합니다.

상속은 인터페이스의 상속, 확장을 뜻하는 것이 아닌 클래스를 상속 받아 확장하는 것으로 제한 하겠습니다.

 

모든 소스 코드는 https://github.com/lkimilhol/tistoryblog 에서 확인 할 수 있습니다.

 

예제를 보도록 하겠습니다. 우리는 카드를 통해 게임을 구현하려 합니다. 이 게임은 카드 뭉치 속에 내가 택한 번호가 있는지를 가려냅니다.

카드 게임을 시작해 보겠습니다

 

1. 어떤 문제가 있을까?


Cards.java

class Cards {
    protected List<Integer> numbers = new ArrayList<>();

    protected Cards() {}

    protected Cards(List<Integer> numbers) {
        this.numbers = numbers;
    }
}

class Game extends Cards {
    private Game() {}

    public Game(List<Integer> numbers) {
        super(numbers);
    }

    public boolean play(int number) {
        return numbers.contains(number);
    }

    public void addAnswer(int number) {
        numbers.add(number);
    }
}

게임이 시작되면 Game 생성자를 통해 카드 번호를 받습니다. 그리고 Cards 클래스인 슈퍼 클래스의 생성자를 호출하여 카드 번호들을 저장합니다. 그리고 play 메서드를 통해 게임이 진행됩니다. 생성자를 통해 넘겼던 카드 번호 들 중 내가 선택 번호가 있으면 되겠네요!

 

위의 클래스에는 몇 가지 문제가 있습니다.

만약 List<Integer>를 배열로 바꾸고 싶다면 어떻게 해야 할까요?

 

이런 문제를 조합을 이용하여 해결 할 수 있을까요?

 

이 정도면 메서드가 별로 없어 다행인 상황일 것입니다

 

2. 조합을 이용해 보자!


일단 코드를 수정해 보도록 할게요. 그리고 어떤 변화들이 있는지 차근 차근 생각해 보도록 합시다.

 

Cards.java

class Cards {
    protected List<Integer> numbers;

    private Cards() {}

    public Cards(List<Integer> numbers) {
        this.numbers = numbers;
    }
    
    public boolean isContain(int number) {
        return numbers.contains(number);
    }
    
    public void add(int number) {
        numbers.add(number);
    }
}

class Game {
    private Cards cards;
    
    private Game() {}

    public Game(Cards cards) {
        this.cards = cards;
    }

    public boolean play(int number) {
        return cards.isContain(number);
    }

    public void addAnswer(int number) {
        cards.add(number);
    }
}

우리는 Cards 라는 클래스를 조합하여 Game 클래스를 재구성했습니다. 이렇게 된다면 Game 클래스 입장에서는 Cards 클래스가 어떻게 구성되어있는지 확인 하지 않아도 되겠네요.

 

Game 클래스에서 Cards의 숫자 혹은 타입 관리를 List를 사용하던, Set을 사용하던, 배열을 사용하던 상관이 없어졌습니다.

만약 상속을 이용하고 있었다면 계속해서 서브 클래스인 Game의 생성자를 수정해줘야 할거에요. 따라서 이 설계가 좀 더 유연하다고 볼 수 있지 않을까요?

 

하위 클래스가 상위 클래스 구현에 영향을 받았었지만 이를 제거 해 버림으로써 상위 클래스는 어떤 구현을 하더라도 상관이 없어진 것입니다. 

 

또 게임 플레이를 숫자 맞추기가 아닌 타입 맞추기로 한다면 하트, 다이아몬드, 스페이스, 클로버가 있으니 List<Integer>를 List<String> 타입으로 변경해여 카드를 생성해야 할텐데요. 이 또한 Cards 클래스에서 수정을 하고 Game 클래스에서는 play의 인자로 String 변경만 하면 될거같네요.

 

(만약 Game 클래스의 play 메서드가 변경 되는 것을 원치 않으신다면 Game 클래스를 삭제 하는것도 방법이 될 수 있습니다. 모든 역할을 Cards 클래스로 위임하는 것이지요. 필요 없는 클래스를 하나 발견 한 것일 수도 있겠네요.)

 

이 외로도 상위 클래스의 구현에 신경을 쓰지 않으니 상위 클래스에선 어떤 메서드가 추가 되던 어떤 기능이 생기던 상관 없습니다. 

 

3. 마치며


조합에 관한 여러 고민들을 하던중 짧게나마 스스로 코드를 작성해 보았는데요. 제가 설명드린 부분 외로도 조합을 이용하여 다른 문제들을 해결 할 수 있다고 생각합니다. 추후에 추가적인 내용이 있다면 또 정리해 보도록 하겠습니다.

 

감사합니다.

'Java' 카테고리의 다른 글

제네릭에 대한 간단한 정리  (0) 2021.10.06
Java의 Call by value, Call by reference  (0) 2021.08.09
간단한 Spring AOP 개념과 적용  (0) 2021.07.30
JPA N+1 문제  (0) 2021.07.22