devFancy BE Developer

[내 코드가 그렇게 이상한가요?] 8 - 12장 정리

2023-11-18
devFancy

이 글은 내 코드가 그렇게 이상한가요? 책을 읽고 정리한 내용을 바탕으로 작성하였습니다.

8장. 강한 결합: 복잡하게 얽혀서 풀 수 없는 구조

소프트웨어 설계에서 결합도는 모듈 사이의 의존도를 나타내는 지표이고, 책무는 어떤 관심사를 정상적으로 작동하게 제어하는 책임이다.

  • 단일 책임 원칙을 기반으로 설계해야 한다.

    • 단일 책임 원칙은 ‘클래스가 담당하는 책임은 하나로 제한해야 한다’는 설계 원칙이다.

    • 책임을 대신 지는 클래스가 만들어지면, 다른 클래스가 제대로 성장할 수 없다. (= 성숙해지지 않는다)

    • 따라서 관심사에 따라 분리해서 독립되어 있는 구조, 즉 느슨한 결합으로 설계해야한다.

  • DRY 원칙(= Don’t Repeat Yourself) : ‘빈복을 피해라’

    • DRY는 각각의 개념 단위내에서 반복을 하지 말라는 의미이다.

    • 같은 로직, 비슷한 로직이라도 개념이 다르면 중복을 허용해야 한다.

    • 무리하게 중복을 제거하려 하면, 강한 결합 상태가 된다.

  • 상속을 하면, 강한 결합 구조를 유발하게 된다. (이 책에서는 상속 자체를 권장하지 않는다)

    • 상속 관계에서 서브 클래스는 슈퍼 클래스에 크게 의존하게 된다. -> 서브 클래스가 슈퍼 클래스의 구조를 하나하나 신경 써야 한다.

    • 강한 결합을 피하려면, 상속보다 컴포지션 을 사용하는 것이 좋다.

    • 컴포지션이란 사용하고 싶은 클래스를 pri-vate 인스턴스 변수로 갖고 사용하는 것을 의미한다.

    • 상속은 전략 패턴 등으로 조건 분기를 줄일 때 활용할 수 있다.

    • 만약 상속을 사용한다면, 반드시 단일 책임 원칙을 염두에 두고 구현해야 하며, 값 객체와 컴포지션 등 다른 설계를 사용할 수는 없는지 검토한다.

  • 관련된 것끼리 클래스로 분리한다.

    • 클래스를 잘 분리하려면, 각각의 인스턴스 변수와 메서드가 무엇과 관련 있는 지 잘 파악해야 한다.
  • 특별한 이유 없이 public을 사용하지 않는다. -> public을 붙이면, 강한 결합 구조가 되어버린다. -> 유지보수가 어려워진다.

    • 클래스는 기본적으로 package private(default)로 만들고, 패키지 외부에 공개할 필요가 있는 클래스에 한해서만 public으로 선언한다.
  • 한 클래스 내에서 private 메서드가 많으면, 책임이 너무 많은 건 아닌지 확인해본다. -> 책임이 다른 메서드는 다른 클래스로 분리한다.

    • 각각의 개념을 각각의 클래스에 잘 분할해서 값 객체로 설계하는 것이 좋다.
  • 스마트 UI는 화면 표시를 담당하는 클래스 중에서 화면 표시와 직접적인 관련이 없는 책무가 구현되어 있는 클래스를 말한다.

  • 트랜잭션 스크립트 패턴은 메서드 내부에 일련의 처리가 하나하나 길게 작성되어 있는 구조이다.

  • 갓 클래스는 하나의 클래스 내부에 수천에서 수만 줄의 로직을 담고 있으며, 수많은 책임을 담당하는 로직이 난잡하게 섞여있는 클래스이다.

  • 강한 결합 클래스 대처 방법 -> 객체 지향 설계와 단일 책임 원칙에 따라 제대로 설계한다.

    • 책임별로 클래스를 분할 -> 단일 책임 원칙에 따라 설계된 클래스는 아무리 많아도 200줄, 일반적으로 100줄 정도이다.

9장. 설계의 건정성을 해치는 여러 악마

  • 데드코드는 절대로 실행되지 않는 조건 내부에 있는 코드를 말한다. -> 발견하는 즉시 제거한다.

  • YAGNI : ‘You Aren’t Gonna Need it.’ -> 지금 필요 없는 기능을 만들지 말라 -> 지금 당장 필요한 것들만 만들라는 방침이다.

  • 매직 넘버는 로직 내부에 직접 작성되어 있어서, 의미를 알기 힘든 숫자를 말한다. -> 구현자 본인만 의도를 이해할 수 있다.

    • 매직 넘버를 사용하지 않으려면, 상수를 활용하면 된다. 예시) private static final int POINT = 100
  • 영향 범위가 가능한 한 되도록 좁게 설계해야 한다. -> 호출할 수 있는 위치가 적고 국소적일수록 로직을 이해하고 구현하기 쉽다.

  • null을 리턴/전달하지 않는다. -> null 대신 static final 인스턴스 변수 EMPTY로 만든다.

  • 예외를 확인했다면, 바로 기록하는 것이 좋다.

    • catch 구문에서는 최소한 로그로 기록하고, 상위 레이어의 클래스로 오류를 통지하는 것이 좋다.
  • 비즈니스 클래스는 비즈니스 개념을 기준으로 폴더를 구분하는 것이 좋다. -> 도메인 별로 구분되어 응집도가 높아진다.

  • 이 책에서 설명하는 방법들은 사양 변경이 있을 때, 이를 조금이라도 쉽게 하기 위한 설계를 설명한다.

    • 설계에 Best라는 건 없다. 항상 Better을 목표로 임한다.

10장. 이름 설계: 구조를 파악할 수 있는 이름

  • 결합이 느슨하고 응집도가 높은 구조로 만들기 위해서는 관심사 분리로, 관심사에 따라서 각각 클래스로 분할해야 한다.

    • 관심사 분리는 ‘관심사(유스케이스, 목적, 역할)에 따라서 분리한다’라는 소프트웨어 공학의 개념이다.
  • 이름 설계하기

    • 최대한 구체적이고, 의미 범위가 좁고, 특화된 이름을 선택하기 -> 어떤 비즈니스를 하는지 모두 파악해야 한다.

    • 존재가 아니라 목적을 기반으로 생각하기 예시) 상품이라면 -> 입고 상품, 예약 상품, 주문 상품, 발송 상품

    • 어떤 관심사가 있는지 분석하기

    • 소리 내어 이야기해 보기 -> 비즈니스 측면을 잘 이해하고 있는 사람과 이야기 해본다. -> 잘못 인식하는 부분이 있으면 바로 피드백을 받을 수 있다.

    • 이용 약관 읽어보기 -> 비즈니스 규칙과 클래스를 일치하게 만든다.

    • 다른 이름으로 대체할 수 없는지 검토하기 -> ‘고객’이 아니라 ‘투숙객’, ‘결제자’처럼 대체할 수 없도록 이름을 변경한다.

    • 결합이 느슨하고 응집도가 높은 구조인지 검토하기

  • 이름을 기반으로 메서드와 클래스를 설계해야 한다. -> 프로그램 구조를 크게 좌우한다.

  • 수식어를 붙이면서까지 차이를 나타내고 싶은 대상은 각각 클래스로 설계하는 것이 좋다. -> 의미가 다른 개념을 서로 다른 클래스로 설계해서 구조화하면, 개념 사이의 관계를 이해하기 쉽다.

    • OriginalMaxHitPoint : ‘캐릭터의 원래 최대 히트 포인트’를 나타내는 클래스

    • CorrectedMaxHitPoint : ‘장비 착용으로 높아진 최대 히트 포인트’를 나타내는 클래스

  • DTO(Data Transfer Object) : 예외적으로 데이터 클래스를 사용하는 경우

    • 데이터 전송 용도로 사용되는 디자인 패턴으로, 참조 용도로만 사용되며, 값을 변경하는 용도로 사용하면 안된다.
  • 메서드의 이름은 동사 하나로 구성되게 한다.

11장. 주석: 유지 보수와 변경의 정확성을 높이는 주석 작성 방법

  • 실제 내용과 내용이 다른 주석은 제거한다.

  • 주석 규칙

    • 로직을 변경할 때는 반드시 주석도 함께 변경해야 한다. -> 주석을 제대로 변경하지 않으면, 실제 로직과 달라져 주석을 읽는 사람에게 혼란을 준다.

    • 로직의 내용을 단순하게 설명하기만 하는 주석은 달지 않는다. -> 시간이 지남에 따라 내용이 낡은 주석이 될 가능성이 높다.

    • 로직의 의도와 사양을 변경할 때 주의할 점을 주석으로 달아야 한다. -> 유지 보수와 사양 변경에 도움이 된다.

  • 문서 주석이란 특정 형식에 맞춰 주석을 작성하면, API 문서를 생성해 주거나 코드 에디터에서 주석의 내용을 팝업으로 표시해 주는 기능입니다.

    • 자바의 Javadoc을 참고하자.

12장. 메서드(함수): 좋은 클래스에는 좋은 메서드가 있다.

  • getter/setter는 값을 마음대로 변경할 수 있으므로 잘못된 값이 섞일 수도 있고, 코드가 중복되는 등 응집도를 낮출 수 있다.

  • 매개변수에 final 수식자를 붙여서, 불변으로 만든다.

    • 매개변수를 변경하고 싶으면, 불변 지역 변수를 만들고, 여기에 변경 값을 할당하는 형태로 구현한다.
  • 출력 매개변수는 사용하지 않는 것이 좋다. -> 가독성 저하

  • 매개변수는 최대한 적게 설계한다. -> 메서드가 처리할 게 많아지면, 그만큼 로직이 복잡해진다.

  • 오류는 특정 값으로 리턴하지 말고, 곧바로 예외를 발생시키는 것이 좋다. -> throw new IllegalArgumentException()

Reference


Comments

Index