내가 객체를 설계하는 과정

Hanjmo·2023년 10월 22일
1

나는 객체지향을 처음 접했을 때, 책에서 알려주는 내용을 냅다 외웠다. 클래스, 객체, 캡슐화, 상속 등등…

시간이 지나 직접 프로그램을 만들어보고싶었던 나는 인텔리제이를 키고나서, 코드를 작성하려고 했지만 문제가 발생했다.

음.. 어떻게 시작해야 하지? 하나의 파일에 모든 코드를 작성하면 객체지향이 아니라 했는데.. 클래스 분리는 어떻게 하라는거지?

하나부터 열까지 너무 막막해서 키보드를 누르기까지 오랜 시간이 걸렸다. 그렇게 객체 설계 기법에 대한 조언을 찾다가 객체지향의 사실과 오해라는 책을 발견했다. 그 책에 대한 무수한 칭찬을 보고 독서를 시작했다. 뒤통수를 한 대 맞은 기분이었다. 나는 여태 무엇을 학습한걸까..

당시의 나와 같이 객체를 어떻게 설계해야 할지 감도 안잡히는 사람들이 분명 존재할 것이라 생각한다. 그들을 위해, 그리고 내가 객체를 설계하는 과정을 글로 풀어냄으로써 정립할 수 있지 않을까 하여 글을 작성해본다.

이 글에서는 코드를 거의 보여주지 않는다. 그저 요구사항을 통해 객체를 설계하는 과정만 보여준다.

우아한테크코스의 숫자 야구 미션을 통해 객체를 설계해보자.

요구 사항 분석

프로그램을 만들기 위해서는 당연히 요구 사항부터 분석해야 한다. 그래야 어떤 기능이 존재하는지 명확하게 이해할 수 있기 때문이다.

나는 요구 사항을 분석할 때 기능 목록을 작성하는 것뿐만 아니라, 프로그램이 어떤 흐름으로 동작할지에 대한 그림을 간단하게 그려본다.

flow

이렇게 프로그램이 실행되는 일련의 과정을 그림으로 나타내면, 프로그램의 전체적인 흐름을 쉽게 파악할 수 있게 된다.
또한 그림을 구체적으로 그릴 수록 미션에 대한 설명을 보지 않고 그림만 봐도 구현이 가능해지는 장점이 있다.

그림은 꼭 나처럼 그리지 않아도 된다. 본인만의 방식으로 자유롭게 그리자. 중요한 것은 요구 사항에 대한 이해다.

🎬 가상의 세계로 바라보자

요구 사항을 어느 정도 분석했다면, 본격적으로 객체를 설계해보자.

객체를 설계하기 위한 첫 번째 단계는 내가 만들 프로그램을 객체들이 살아 숨쉬는 가상의 세계로 바라보는 것이다.

어렵게 생각할 것 없이, 가상 세계에 있는 객체들은 모두 우리같이 자율적인 판단과 능동적인 행동을 할 수 있는 존재라고 생각하면 된다. 누구나 한 번쯤 상상의 나래를 펼치며 과몰입해본 경험이 있지 않나?

하여튼 우리가 지금 해결해야 할 예제는 숫자 야구이므로, 우선 숫자 야구라는 세계에서 어떠한 사건이 일어날지 나열해보자. (나는 친구와 숫자 야구를 했던 경험을 살리며 사건을 나열해봤다.)

여기서 사건은 “컴퓨터의 숫자가 플레이어의 숫자를 비교한다.”와 같은 것을 말한다. 이를 위해 우리는 앞서 요구 사항을 분석하며 그림을 그렸던 것이다.

그림을 보면 몇 개의 큰 사건들이 보인다. 함께 나열해보자.

  1. 컴퓨터의 숫자가 생성된다.
  2. 플레이어의 숫자가 생성된다.
  3. 컴퓨터의 숫자와 플레이어의 숫자를 비교하면서 볼과 스트라이크를 카운트한다.

나는 3개의 사건을 발견했는데, 더 적게 발견했거나 더 많게 발견했어도 틀린 것은 아니다. 나와 보는 것이 다를 뿐, 각자의 시점으로 이어 나가면 된다.

이제 각각의 사건에서 어떤 객체들이 무슨 일을 하면서 협력하고 있을지 상상해보자.

여기서 중요한 점은 객체가 누구냐에 집중하기보다, 객체들이 어떤 메시지를 주고받는지 에 집중해야 한다는 것이다. 메시지를 정하고나서 각 메시지를 수신할 객체를 선택하는 것이다.

메시지란 말 그대로 “누구누구야 이것 좀 해줘.”와 같은 객체 간 의사소통이라고 보면 된다.

자세한 건 객체지향의 사실과 오해 또는 여기를 참고하자.

1️⃣ 컴퓨터의 수가 생성되는 과정

컴퓨터의 수가 생성되는 과정에서 나올 발견할 수 있는 메시지는 당연히 “컴퓨터의 수를 생성해줘.”일 것이다.

이 메시지는 누구에게 보내야 할까?

컴퓨터의 수를 생성하는 일을 담당하는 ComputerNumbersGenerator 객체에게 보내면 될 것 같다. ComputerNumbersGenerator는 메시지에 응답하여 컴퓨터의 수를 생성한다.

벌써 ComputerNumbersGenerator라는 객체를 하나 발견했다. 다음 사건으로 넘어가자.

2️⃣ 플레이어의 수를 생성하는 과정

플레이어의 수는 사용자의 입력으로부터 생성되므로, “플레이어의 수를 생성해줘.”라는 메시지를 InputView라는 객체에게 보낸다. InputView는 메시지에 응답하기 위해 사용자로부터 3자리의 숫자를 입력받아 반환한다.

두번째 객체인 InputView를 발견했다.

3️⃣ 플레이어의 수를 체크하는 과정

마지막은 플레이어의 수를 체크하는 과정인데, 여기서는 앞에서와 다르게 약간의 고민을 거쳐야 한다.

가장 먼저 생각나는 메시지는 “플레이어의 수를 체크해줘.”다. 그런데 이 메시지를 누구에게 보내야 할까?

나는 여기서 친구와 숫자 야구를 했던 경험을 떠올렸다.

내가 출제자일 때, 친구는 나에게 어떤 숫자를 말하면서 맞는지 비교해달라고 요청한다. 이를 우리가 상상하는 가상의 세계에 대입해보자.

플레이어의 수는 자신이 가진 각각의 숫자를 컴퓨터의 수와 비교해서 정답 여부를 알고싶을 것이다. 따라서, 컴퓨터의 수에게 본인의 숫자를 넘기면서 볼과 스트라이크를 카운트 해달라고 요청한다.

이를 통해 “플레이어의 수를 체크해줘.” 메시지의 수신자는 컴퓨터의 수 ComputerNumbers로 선택할 수 있다.

플레이어의 숫자를 받은 ComputerNumbers는 자신이 가진 숫자와 하나씩 비교하면서 볼과 스트라이크를 카운트한다.

이처럼 우리가 보는 가상의 세계에서 ComputerNumbersGenerator, InputView, ComputerNumbers라는 객체들이 서로 협력하고 있다는 것을 알 수 있다. (이외에도 “시작, 종료 문구를 출력해줘”라는 메시지의 수신자로 OutputView를 선택할 수도 있겠다.)

객체들이 어떤 메시지를 송수신하면서 협력하는지 찾았다면, 자연스럽게 아래와 같은 클래스들이 보일 것이다.

각 클래스별로 가지는 변수와 메서드는?

클래스는 작성했는데, 각 클래스별로 어떤 변수와 메서드를 가지는지는 아직 감이 안잡힐 수도 있다.

이에 대한 해답은 우리가 객체를 뽑아내는 과정에 있다. 사건을 나열하고, 그 속에서 협력을 찾아내면서 우리는 무엇을 찾았지? 바로 메시지다.

메시지는 객체가 수행할 행동을 포함하고 있다.

예를 들어, “플레이어의 수를 체크해줘”라는 메시지를 받은 ComputerNumbers는 응답하기 위해 어떤 행동을 수행할 것이다. 이 행동이 메서드라고 보면 된다.

즉, 아래와 같이 코드를 작성할 수 있다.

public class ComputerNumbers {

    public void checkPlayerNumbers(PlayerNumbers playerNumbers, int digitNumber) {
		...
    }

...

이번에는 변수를 찾아보자. 컴퓨터의 수는 플레이어의 수를 무엇과 비교하지? 자신의 숫자들과 비교한다. 이와 같이 어떤 행동을 수행하기 위해서 가지는 상태가 바로 변수다.

이에 대한 코드는 다음과 같다.

public class ComputerNumbers {

	private final List<Integer> numbers;

    public ComputerNumbers(List<Integer> numbers) {
        this.numbers = numbers;
    }

    public void checkPlayerNumbers(PlayerNumbers playerNumbers, int digitNumber) {
		...
    }

...

끝맺음

지금까지 내가 객체를 설계하는 과정이었다. 이제 내가 언급했던 책과 블로그 글들을 열심히 참고하면서 직접 연습해본다면, 복잡했던 객체지향에 대한 개념들이 슬슬 눈에 잘 보이게 될 것이다.

아, 물론 이렇게 설계하는 과정을 거쳐도 구현하다보면 이곳저곳에서 바뀌면서 난리가 날 것이다. 하지만 당황하지 말고, 막힌 곳에서부터 차근차근 다시 설계해보자. 설계는 그저 내 상상일 뿐이고, 구현을 위한 도구일 뿐이다.

설계에 충분한 시간을 들이되, 너무 많은 힘을 쏟지 말자.

0개의 댓글