[회고] 우아한 테크코스 5기 프리코스 3주차 회고

TAEYONG KIM·2022년 11월 16일
0

우아한 테크 코스

목록 보기
2/3

회고를 시작하며...


객체지향 원칙을 조금이나마 지켜나가면서 코드를 작성하는 것! 이게 이렇게도 어려운 일이었던가? 컴퓨터공학을 전공한 학생으로서 이번 미션은 스스로 부끄러운 시간이기도 했다.

먼저 회고의 시작은 클래스 분리와 메서드가 최대한 한 가지 일을 하도록 분리하는 것이다.

📝필자는 "메서드가 한가지 기능을 수행하도록 만들자!" 에 대해서는 다음과 같이 이해했다. 예를 들어서, 하나의 메서드에서 객체에 값을 새롭게 추가하는 역할을 하는 것과 동시에 조건을 통해서 값의 업데이트가 일어난다면 어떻게 될까? -> 이슈가 발생했을 때, 유지 보수하는 시간을 많이 사용할 것이다. 이처럼 이러한 사례를 예방하기 위해서 분리하는 작업이 필요하다고 생각한다

가독성과 재사용성에 대해서 좋은 조건이지 않을까?


📝 다음은 클래스에 대한 분리는 다음과 같이 이해했습니다.
하나의 클래스에서 클래스 목적과 관련 없는 기능들이 나열되어 있으면 클래스는 하나의 기능을 핵심으로 수행한다는 점에 있어서 객체 지향원칙에 벗어난다고 생각해요.

클래스 -> 목적에 맞지 않게 여러 개의 기능을 수행하고 있다가 소프트웨어에서 이 부분에 대해 이슈가 발생했을 때 해당 클래스와 관련된 코드들에 대해 전체적으로 수정할 확률이 높다고 생각합니다. 따라서 객체지향 생활 원칙 중, 일급 컬렉션 활용에 대해 이번 미션을 집중했습니다.

요구 사항 중, Lotto 클래스에서 인스턴스 변수를 추가할 수 없으며

class Lotto {
	List<Integer> numbers;
    // 기능 구현...
}

Lotto라는 클래스는 정수로 이루어진 컬렉션을 가지고 있는 형태를 주었습니다.

즉 제가 구현한 Lotto 클래스는 numbers 에대해 스스로 책임을 가지도록 구현하였습니다. Lotto라는 클래스를 생성함에 있어서

  • 숫자로 이루어진 리스트 검증
  • 숫자 범위 1 ~ 45 검증
  • 중복된 숫자가 존재하는지 검증
    등등 ... 생성함에 있어서예외 발생에 대해 검증을 보장하고 불변을 보장할 수 있다는 의미입니다.

💁‍♂️ 이러한 형태가 어떤 장점을 줄까?


이번 미션에 대해서는 로또 구매자에 대해 Buyer 클래스 한 곳에서만 사용하지만 여러 클래스에서 Lotto를 생성하여 사용한다고 하면 장점이 존재합니다.
(제 개인적인 의견이니 조금 더 공유를 해주실 수 있다면 더 좋을 것 같아요!)

이전에 2주차 미션에서는 CheckValidation 클래스를 생성하여 검증 로직을 몰두했습니다. 이러면 로또를 생성할 때마다 CheckValidation을 통해서 검증을 진행하는 로직이 추가로 계속 작성해야 합니다. 가독성은 모르겠지만 같은 일 처리를 여러 번 해야 한다는 생각이 들지 않나요?

따라서 위의 일급 컬렉션을 이용하면 로또 생성과 동시에 검증 및 불변성을 한 번에 제공해줍니다.
이해가 가도록 설명했는지는 모르겠네요…. ㅎ

public class Lotto {
    private final List<Integer> numbers;

    public Lotto(List<Integer> numbers) {
        List<Integer> mutable = new ArrayList<>(numbers);
        validateLength(mutable);
        validateOverlap(mutable);
        sortByAscending(mutable);
        this.numbers = mutable;
    }

    private void validateLength(List<Integer> numbers) {
        if (numbers.size() != Statistic.SAME_SIX.getValue()) {
            throw new IllegalArgumentException();
        }
    }

    private void validateOverlap(List<Integer> numbers) {
        if (getDistinctLength(numbers) != Statistic.SAME_SIX.getValue()) {
            throw new IllegalArgumentException();
        }
    }

    private int getDistinctLength(List<Integer> numbers) {
        return (int)numbers.stream().distinct().count();
    }

    private void sortByAscending(List<Integer> numbers) {
        Collections.sort(numbers);
    }
}

Stream API를 사용하면서

필자의 개인적인 의견이지만 Stream API를 사용하는 것은 좋은데 동작 원리를 어느 정도 이해하고 넘어가는 것이 어떤 기능을 사용하든지 정답이라고 생각한다.

왜냐하면, 필자도 같은 실수를 했습니다. 단순히 어느 정도 기능 결괏값을 알고 있다면 기계적으로 외워서 사용하는 자신을 돌아보았기 때문이다. 이번 미션을 통해서 본질을 이해하는 데 집중하게 되어 객체지향부터 시작해서 Stream까지 배움의 시간을 가지게 되었습니다.

직관적으로 이해할 수 있는 코드?

stream을 공부하고 나서 얻은 점은 중간 연산과 최종 연산을 이해했다면 기능을 이해하기 쉽다는 것입니다. for 문과 if 문 등 분기가 여러 개로 이루어져 있다면 해석하는 데 오랜 시간이 걸린다고 생각합니다. Indent를 2 이상 주지 않는 것에 대해 조금 더 활용할 수 있지 않을까 생각합니다.

코드를 작성한다는 것?

미션을 진행하고 나서 피어 리뷰를 할 수 있다는 점을 통해서 미션을 수행하는 분들에게 얻어가는 점도 많고 배움의 시간이 많습니다. 코드를 작성하면서 가독성을 높이는 일은 쉽게 말해서 글을 읽을 때 독해의 효율을 높이는 일과 같다라고 생각합니다. 실력이 좋으신 분들의 특징 중 하나가 이러한 점에서 배울 점이 많은 것 같아요!

회고를 마치며..

다음 회고는 마지막 프리코스의 회고가 될 수 있지 않을까 싶은데 테스트 코드에 대한 회고와 객체지향의 원칙을 맞추어 조금 더 고도화된 코드 작성에 대해 회고할까 합니다.

간단한 기능이라도 정말 많은 요구사항과 예외에 대해 생각하는 힘을 기르는 시간도 가지게 된 것 같아요!

끝으로…. 어렵더라도 스스로 학습하는 시간은 언제나 유익한 것 같습니다!

모두 화이팅

profile
백엔드 개발자의 성장기

1개의 댓글

comment-user-thumbnail
2022년 11월 16일

잘보고갑니다!

답글 달기